Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2019-03-23 02:39:52 +0300
committerJoyee Cheung <joyeec9h3@gmail.com>2019-04-06 07:01:45 +0300
commit864860e9f3d4eed0b0b81af55197d7e525ea6306 (patch)
treeed0c9cbc1cea99323cd4fa4c5385a11b262be8ee /src/inspector_profiler.cc
parentbaa54a5ae78ff04a3e8d8ac97c052304a6f6c18c (diff)
src: port coverage serialization to C++
This patch moves the serialization of coverage profiles into C++. With this we no longer need to patch `process.reallyExit` and hook into the exit events, but instead hook into relevant places in C++ which are safe from user manipulation. This also makes the code easier to reuse for other types of profiles. PR-URL: https://github.com/nodejs/node/pull/26874 Reviewed-By: Ben Coe <bencoe@gmail.com>
Diffstat (limited to 'src/inspector_profiler.cc')
-rw-r--r--src/inspector_profiler.cc301
1 files changed, 153 insertions, 148 deletions
diff --git a/src/inspector_profiler.cc b/src/inspector_profiler.cc
index 942a1d148a2..27b1db14c92 100644
--- a/src/inspector_profiler.cc
+++ b/src/inspector_profiler.cc
@@ -1,6 +1,7 @@
+#include "inspector_profiler.h"
#include "base_object-inl.h"
#include "debug_utils.h"
-#include "inspector_agent.h"
+#include "node_file.h"
#include "node_internals.h"
#include "v8-inspector.h"
@@ -8,7 +9,6 @@ namespace node {
namespace profiler {
using v8::Context;
-using v8::EscapableHandleScope;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::HandleScope;
@@ -17,188 +17,192 @@ using v8::Local;
using v8::MaybeLocal;
using v8::NewStringType;
using v8::Object;
-using v8::ObjectTemplate;
using v8::String;
using v8::Value;
using v8_inspector::StringBuffer;
using v8_inspector::StringView;
+#ifdef __POSIX__
+const char* const kPathSeparator = "/";
+#else
+const char* const kPathSeparator = "\\/";
+#endif
+
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
Local<Value> value) {
TwoByteValue buffer(isolate, value);
return StringBuffer::create(StringView(*buffer, buffer.length()));
}
-class V8ProfilerConnection : public BaseObject {
- public:
- class V8ProfilerSessionDelegate
- : public inspector::InspectorSessionDelegate {
- public:
- explicit V8ProfilerSessionDelegate(V8ProfilerConnection* connection)
- : connection_(connection) {}
-
- void SendMessageToFrontend(
- const v8_inspector::StringView& message) override {
- Environment* env = connection_->env();
-
- Local<Function> fn = connection_->GetMessageCallback();
- bool ending = !fn.IsEmpty();
- Debug(env,
- DebugCategory::INSPECTOR_PROFILER,
- "Sending message to frontend, ending = %s\n",
- ending ? "true" : "false");
- if (!ending) {
- return;
- }
- Isolate* isolate = env->isolate();
-
- HandleScope handle_scope(isolate);
- Context::Scope context_scope(env->context());
- MaybeLocal<String> v8string =
- String::NewFromTwoByte(isolate,
- message.characters16(),
- NewStringType::kNormal,
- message.length());
- Local<Value> args[] = {v8string.ToLocalChecked().As<Value>()};
- USE(fn->Call(
- env->context(), connection_->object(), arraysize(args), args));
- }
-
- private:
- V8ProfilerConnection* connection_;
- };
-
- SET_MEMORY_INFO_NAME(V8ProfilerConnection)
- SET_SELF_SIZE(V8ProfilerConnection)
-
- void MemoryInfo(MemoryTracker* tracker) const override {
- tracker->TrackFieldWithSize(
- "session", sizeof(*session_), "InspectorSession");
- }
+V8ProfilerConnection::V8ProfilerConnection(Environment* env)
+ : session_(env->inspector_agent()->Connect(
+ std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>(
+ this),
+ false)),
+ env_(env) {}
- explicit V8ProfilerConnection(Environment* env, Local<Object> obj)
- : BaseObject(env, obj), session_(nullptr) {
- inspector::Agent* inspector = env->inspector_agent();
- std::unique_ptr<inspector::InspectorSession> session = inspector->Connect(
- std::make_unique<V8ProfilerSessionDelegate>(this), false);
- session_ = std::move(session);
- MakeWeak();
- }
+void V8ProfilerConnection::DispatchMessage(Local<String> message) {
+ session_->Dispatch(ToProtocolString(env()->isolate(), message)->string());
+}
- void DispatchMessage(Isolate* isolate, Local<String> message) {
- session_->Dispatch(ToProtocolString(isolate, message)->string());
+bool V8ProfilerConnection::WriteResult(const char* path, Local<String> result) {
+ int ret = WriteFileSync(env()->isolate(), path, result);
+ if (ret != 0) {
+ char err_buf[128];
+ uv_err_name_r(ret, err_buf, sizeof(err_buf));
+ fprintf(stderr, "%s: Failed to write file %s\n", err_buf, path);
+ return false;
}
+ Debug(
+ env(), DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path);
+ return true;
+}
- static MaybeLocal<Object> CreateConnectionObject(Environment* env) {
- Isolate* isolate = env->isolate();
- Local<Context> context = env->context();
- EscapableHandleScope scope(isolate);
-
- Local<ObjectTemplate> t = ObjectTemplate::New(isolate);
- t->SetInternalFieldCount(1);
- Local<Object> obj;
- if (!t->NewInstance(context).ToLocal(&obj)) {
- return MaybeLocal<Object>();
- }
-
- obj->SetAlignedPointerInInternalField(0, nullptr);
- return scope.Escape(obj);
+void V8CoverageConnection::OnMessage(const v8_inspector::StringView& message) {
+ Debug(env(),
+ DebugCategory::INSPECTOR_PROFILER,
+ "Receive coverage message, ending = %s\n",
+ ending_ ? "true" : "false");
+ if (!ending_) {
+ return;
}
-
- void Start() {
- SetConnection(object());
- StartProfiling();
+ Isolate* isolate = env()->isolate();
+ Local<Context> context = env()->context();
+ HandleScope handle_scope(isolate);
+ Context::Scope context_scope(context);
+ Local<String> result;
+ if (!String::NewFromTwoByte(isolate,
+ message.characters16(),
+ NewStringType::kNormal,
+ message.length())
+ .ToLocal(&result)) {
+ fprintf(stderr, "Failed to covert coverage message\n");
}
+ WriteCoverage(result);
+}
- void End(Local<Function> callback) {
- SetMessageCallback(callback);
- EndProfiling();
+bool V8CoverageConnection::WriteCoverage(Local<String> message) {
+ const std::string& directory = env()->coverage_directory();
+ CHECK(!directory.empty());
+ uv_fs_t req;
+ int ret = fs::MKDirpSync(nullptr, &req, directory, 0777, nullptr);
+ uv_fs_req_cleanup(&req);
+ if (ret < 0 && ret != UV_EEXIST) {
+ char err_buf[128];
+ uv_err_name_r(ret, err_buf, sizeof(err_buf));
+ fprintf(stderr,
+ "%s: Failed to create coverage directory %s\n",
+ err_buf,
+ directory.c_str());
+ return false;
}
- // Override this to return a JS function that gets called with the message
- // sent from the session.
- virtual Local<Function> GetMessageCallback() = 0;
- virtual void SetMessageCallback(Local<Function> callback) = 0;
- // Use DispatchMessage() to dispatch necessary inspector messages
- virtual void StartProfiling() = 0;
- virtual void EndProfiling() = 0;
- virtual void SetConnection(Local<Object> connection) = 0;
-
- private:
- std::unique_ptr<inspector::InspectorSession> session_;
-};
-
-class V8CoverageConnection : public V8ProfilerConnection {
- public:
- explicit V8CoverageConnection(Environment* env)
- : V8ProfilerConnection(env,
- CreateConnectionObject(env).ToLocalChecked()) {}
-
- Local<Function> GetMessageCallback() override {
- return env()->on_coverage_message_function();
+ std::string thread_id = std::to_string(env()->thread_id());
+ std::string pid = std::to_string(uv_os_getpid());
+ std::string timestamp = std::to_string(
+ static_cast<uint64_t>(GetCurrentTimeInMicroseconds() / 1000));
+ char filename[1024];
+ snprintf(filename,
+ sizeof(filename),
+ "coverage-%s-%s-%s.json",
+ pid.c_str(),
+ timestamp.c_str(),
+ thread_id.c_str());
+ std::string target = directory + kPathSeparator + filename;
+ MaybeLocal<String> result = GetResult(message);
+ if (result.IsEmpty()) {
+ return false;
}
+ return WriteResult(target.c_str(), result.ToLocalChecked());
+}
- void SetMessageCallback(Local<Function> callback) override {
- return env()->set_on_coverage_message_function(callback);
+MaybeLocal<String> V8CoverageConnection::GetResult(Local<String> message) {
+ Local<Context> context = env()->context();
+ Isolate* isolate = env()->isolate();
+ Local<Value> parsed;
+ if (!v8::JSON::Parse(context, message).ToLocal(&parsed) ||
+ !parsed->IsObject()) {
+ fprintf(stderr, "Failed to parse coverage result as JSON object\n");
+ return MaybeLocal<String>();
}
- static V8ProfilerConnection* GetConnection(Environment* env) {
- return Unwrap<V8CoverageConnection>(env->coverage_connection());
+ Local<Value> result_v;
+ if (!parsed.As<Object>()
+ ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result"))
+ .ToLocal(&result_v)) {
+ fprintf(stderr, "Failed to get result from coverage message\n");
+ return MaybeLocal<String>();
}
- void SetConnection(Local<Object> obj) override {
- env()->set_coverage_connection(obj);
+ if (result_v->IsUndefined()) {
+ fprintf(stderr, "'result' from coverage message is undefined\n");
+ return MaybeLocal<String>();
}
- void StartProfiling() override {
- Debug(env(),
- DebugCategory::INSPECTOR_PROFILER,
- "Sending Profiler.startPreciseCoverage\n");
- Isolate* isolate = env()->isolate();
- Local<String> enable = FIXED_ONE_BYTE_STRING(
- isolate, "{\"id\": 1, \"method\": \"Profiler.enable\"}");
- Local<String> start = FIXED_ONE_BYTE_STRING(
- isolate,
- "{"
- "\"id\": 2,"
- "\"method\": \"Profiler.startPreciseCoverage\","
- "\"params\": {\"callCount\": true, \"detailed\": true}"
- "}");
- DispatchMessage(isolate, enable);
- DispatchMessage(isolate, start);
+ Local<String> result_s;
+ if (!v8::JSON::Stringify(context, result_v).ToLocal(&result_s)) {
+ fprintf(stderr, "Failed to stringify coverage result\n");
+ return MaybeLocal<String>();
}
- void EndProfiling() override {
- Debug(env(),
- DebugCategory::INSPECTOR_PROFILER,
- "Sending Profiler.takePreciseCoverage\n");
- Isolate* isolate = env()->isolate();
- Local<String> end =
- FIXED_ONE_BYTE_STRING(isolate,
- "{"
- "\"id\": 3,"
- "\"method\": \"Profiler.takePreciseCoverage\""
- "}");
- DispatchMessage(isolate, end);
- }
+ return result_s;
+}
- private:
- std::unique_ptr<inspector::InspectorSession> session_;
-};
+void V8CoverageConnection::Start() {
+ Debug(env(),
+ DebugCategory::INSPECTOR_PROFILER,
+ "Sending Profiler.startPreciseCoverage\n");
+ Isolate* isolate = env()->isolate();
+ Local<String> enable = FIXED_ONE_BYTE_STRING(
+ isolate, R"({"id": 1, "method": "Profiler.enable"})");
+ Local<String> start = FIXED_ONE_BYTE_STRING(isolate, R"({
+ "id": 2,
+ "method": "Profiler.startPreciseCoverage",
+ "params": { "callCount": true, "detailed": true }
+ })");
+ DispatchMessage(enable);
+ DispatchMessage(start);
+}
+
+void V8CoverageConnection::End() {
+ CHECK_EQ(ending_, false);
+ ending_ = true;
+ Debug(env(),
+ DebugCategory::INSPECTOR_PROFILER,
+ "Sending Profiler.takePreciseCoverage\n");
+ Isolate* isolate = env()->isolate();
+ HandleScope scope(isolate);
+ Local<String> end = FIXED_ONE_BYTE_STRING(isolate, R"({
+ "id": 3,
+ "method": "Profiler.takePreciseCoverage"
+ })");
+ DispatchMessage(end);
+}
+
+// For now, we only support coverage profiling, but we may add more
+// in the future.
+void EndStartedProfilers(Environment* env) {
+ Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n");
+ V8ProfilerConnection* connection = env->coverage_connection();
+ if (connection != nullptr && !connection->ending()) {
+ Debug(
+ env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
+ connection->End();
+ }
+}
void StartCoverageCollection(Environment* env) {
- V8CoverageConnection* connection = new V8CoverageConnection(env);
- connection->Start();
+ CHECK_NULL(env->coverage_connection());
+ env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
+ env->coverage_connection()->Start();
}
-static void EndCoverageCollection(const FunctionCallbackInfo<Value>& args) {
+static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) {
+ CHECK(args[0]->IsString());
Environment* env = Environment::GetCurrent(args);
- CHECK(args[0]->IsFunction());
- Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n");
- V8ProfilerConnection* connection = V8CoverageConnection::GetConnection(env);
- CHECK_NOT_NULL(connection);
- connection->End(args[0].As<Function>());
+ node::Utf8Value directory(env->isolate(), args[0].As<String>());
+ env->set_coverage_directory(*directory);
}
static void Initialize(Local<Object> target,
@@ -206,8 +210,9 @@ static void Initialize(Local<Object> target,
Local<Context> context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
- env->SetMethod(target, "endCoverage", EndCoverageCollection);
+ env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory);
}
+
} // namespace profiler
} // namespace node