From 58ba9527dc5fd3343f719b11a4cef73d23215253 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 29 Apr 2019 19:45:33 +0800 Subject: src: refactor V8ProfilerConnection to be more reusable Now the subclasses only need to override methods to - Get the profile node from the profiler response message object - Return the directory and file path And the V8ProfilerConnection() would handle the rest of profile serialization and message parsing. This makes it easier to add other types of profiles in the future. PR-URL: https://github.com/nodejs/node/pull/27475 Refs: https://github.com/nodejs/node/issues/27421 Reviewed-By: Anna Henningsen Reviewed-By: Ben Noordhuis --- src/inspector_profiler.cc | 234 +++++++++++++++++++++------------------------- 1 file changed, 109 insertions(+), 125 deletions(-) (limited to 'src/inspector_profiler.cc') diff --git a/src/inspector_profiler.cc b/src/inspector_profiler.cc index a50ca0f164..530636f4d0 100644 --- a/src/inspector_profiler.cc +++ b/src/inspector_profiler.cc @@ -50,45 +50,54 @@ void V8ProfilerConnection::DispatchMessage(Local message) { session_->Dispatch(ToProtocolString(env()->isolate(), message)->string()); } -bool V8ProfilerConnection::WriteResult(const char* path, Local result) { - int ret = WriteFileSync(env()->isolate(), path, result); +static void WriteResult(Environment* env, + const char* path, + Local 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; + return; } - Debug( - env(), DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path); - return true; + Debug(env, DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path); } -void V8CoverageConnection::OnMessage(const v8_inspector::StringView& message) { - Debug(env(), +void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend( + const v8_inspector::StringView& message) { + Environment* env = connection_->env(); + Isolate* isolate = env->isolate(); + HandleScope handle_scope(isolate); + Context::Scope context_scope(env->context()); + + // TODO(joyeecheung): always parse the message so that we can use the id to + // identify ending messages as well as printing the message in the debug + // output when there is an error. + const char* type = connection_->type(); + Debug(env, DebugCategory::INSPECTOR_PROFILER, - "Receive coverage message, ending = %s\n", - ending_ ? "true" : "false"); - if (!ending_) { + "Receive %s profile message, ending = %s\n", + type, + connection_->ending() ? "true" : "false"); + if (!connection_->ending()) { return; } - Isolate* isolate = env()->isolate(); - Local context = env()->context(); - HandleScope handle_scope(isolate); - Context::Scope context_scope(context); - Local result; + + // Convert StringView to a Local. + Local message_str; if (!String::NewFromTwoByte(isolate, message.characters16(), NewStringType::kNormal, message.length()) - .ToLocal(&result)) { - fprintf(stderr, "Failed to covert coverage message\n"); + .ToLocal(&message_str)) { + fprintf(stderr, "Failed to covert %s profile message\n", type); + return; } - WriteCoverage(result); + + connection_->WriteProfile(message_str); } -bool V8CoverageConnection::WriteCoverage(Local message) { - const std::string& directory = env()->coverage_directory(); - CHECK(!directory.empty()); +static bool EnsureDirectory(const std::string& directory, const char* type) { uv_fs_t req; int ret = fs::MKDirpSync(nullptr, &req, directory, 0777, nullptr); uv_fs_req_cleanup(&req); @@ -96,12 +105,16 @@ bool V8CoverageConnection::WriteCoverage(Local message) { char err_buf[128]; uv_err_name_r(ret, err_buf, sizeof(err_buf)); fprintf(stderr, - "%s: Failed to create coverage directory %s\n", + "%s: Failed to create %s profile directory %s\n", err_buf, + type, directory.c_str()); return false; } + return true; +} +std::string V8CoverageConnection::GetFilename() const { 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( @@ -113,44 +126,79 @@ bool V8CoverageConnection::WriteCoverage(Local message) { pid.c_str(), timestamp.c_str(), thread_id.c_str()); - std::string target = directory + kPathSeparator + filename; - MaybeLocal result = GetResult(message); - if (result.IsEmpty()) { - return false; - } - return WriteResult(target.c_str(), result.ToLocalChecked()); + return filename; } -MaybeLocal V8CoverageConnection::GetResult(Local message) { - Local context = env()->context(); - Isolate* isolate = env()->isolate(); +static MaybeLocal ParseProfile(Environment* env, + Local message, + const char* type) { + Local context = env->context(); + Isolate* isolate = env->isolate(); + + // Get message.result from the response Local parsed; if (!v8::JSON::Parse(context, message).ToLocal(&parsed) || !parsed->IsObject()) { - fprintf(stderr, "Failed to parse coverage result as JSON object\n"); - return MaybeLocal(); + fprintf(stderr, "Failed to parse %s profile result as JSON object\n", type); + return MaybeLocal(); } Local result_v; if (!parsed.As() ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result")) .ToLocal(&result_v)) { - fprintf(stderr, "Failed to get result from coverage message\n"); - return MaybeLocal(); + fprintf(stderr, "Failed to get 'result' from %s profile message\n", type); + return MaybeLocal(); } - if (result_v->IsUndefined()) { - fprintf(stderr, "'result' from coverage message is undefined\n"); - return MaybeLocal(); + if (!result_v->IsObject()) { + fprintf( + stderr, "'result' from %s profile message is not an object\n", type); + return MaybeLocal(); } + return result_v.As(); +} + +void V8ProfilerConnection::WriteProfile(Local message) { + Local context = env_->context(); + + // Get message.result from the response. + Local result; + if (!ParseProfile(env_, message, type()).ToLocal(&result)) { + return; + } + // Generate the profile output from the subclass. + Local profile; + if (!GetProfile(result).ToLocal(&profile)) { + return; + } Local result_s; - if (!v8::JSON::Stringify(context, result_v).ToLocal(&result_s)) { - fprintf(stderr, "Failed to stringify coverage result\n"); - return MaybeLocal(); + if (!v8::JSON::Stringify(context, profile).ToLocal(&result_s)) { + fprintf(stderr, "Failed to stringify %s profile result\n", type()); + return; } - return result_s; + // Create the directory if necessary. + std::string directory = GetDirectory(); + DCHECK(!directory.empty()); + if (!EnsureDirectory(directory, type())) { + return; + } + + std::string filename = GetFilename(); + DCHECK(!filename.empty()); + std::string path = directory + kPathSeparator + filename; + + WriteResult(env_, path.c_str(), result_s); +} + +MaybeLocal V8CoverageConnection::GetProfile(Local result) { + return result; +} + +std::string V8CoverageConnection::GetDirectory() const { + return env()->coverage_directory(); } void V8CoverageConnection::Start() { @@ -184,92 +232,28 @@ void V8CoverageConnection::End() { DispatchMessage(end); } -void V8CpuProfilerConnection::OnMessage( - const v8_inspector::StringView& message) { - Debug(env(), - DebugCategory::INSPECTOR_PROFILER, - "Receive cpu profiling message, ending = %s\n", - ending_ ? "true" : "false"); - if (!ending_) { - return; - } - Isolate* isolate = env()->isolate(); - HandleScope handle_scope(isolate); - Local context = env()->context(); - Context::Scope context_scope(context); - Local result; - if (!String::NewFromTwoByte(isolate, - message.characters16(), - NewStringType::kNormal, - message.length()) - .ToLocal(&result)) { - fprintf(stderr, "Failed to convert profiling message\n"); - } - WriteCpuProfile(result); +std::string V8CpuProfilerConnection::GetDirectory() const { + return env()->cpu_prof_dir(); } -void V8CpuProfilerConnection::WriteCpuProfile(Local message) { - const std::string& filename = env()->cpu_prof_name(); - const std::string& directory = env()->cpu_prof_dir(); - CHECK(!filename.empty()); - 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 cpu profile directory %s\n", - err_buf, - directory.c_str()); - return; - } - MaybeLocal result = GetResult(message); - std::string target = directory + kPathSeparator + filename; - if (!result.IsEmpty()) { - WriteResult(target.c_str(), result.ToLocalChecked()); - } +std::string V8CpuProfilerConnection::GetFilename() const { + return env()->cpu_prof_name(); } -MaybeLocal V8CpuProfilerConnection::GetResult(Local message) { - Local context = env()->context(); - Isolate* isolate = env()->isolate(); - Local parsed; - if (!v8::JSON::Parse(context, message).ToLocal(&parsed) || - !parsed->IsObject()) { - fprintf(stderr, "Failed to parse CPU profile result as JSON object\n"); - return MaybeLocal(); - } - - Local result_v; - if (!parsed.As() - ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "result")) - .ToLocal(&result_v)) { - fprintf(stderr, "Failed to get result from CPU profile message\n"); - return MaybeLocal(); - } - - if (!result_v->IsObject()) { - fprintf(stderr, "'result' from CPU profile message is not an object\n"); - return MaybeLocal(); - } - +MaybeLocal V8CpuProfilerConnection::GetProfile(Local result) { Local profile_v; - if (!result_v.As() - ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "profile")) + if (!result + ->Get(env()->context(), + FIXED_ONE_BYTE_STRING(env()->isolate(), "profile")) .ToLocal(&profile_v)) { fprintf(stderr, "'profile' from CPU profile result is undefined\n"); - return MaybeLocal(); + return MaybeLocal(); } - - Local result_s; - if (!v8::JSON::Stringify(context, profile_v).ToLocal(&result_s)) { - fprintf(stderr, "Failed to stringify CPU profile result\n"); - return MaybeLocal(); + if (!profile_v->IsObject()) { + fprintf(stderr, "'profile' from CPU profile result is not an Object\n"); + return MaybeLocal(); } - - return result_s; + return profile_v.As(); } void V8CpuProfilerConnection::Start() { @@ -298,16 +282,16 @@ void V8CpuProfilerConnection::End() { // in the future. void EndStartedProfilers(Environment* env) { Debug(env, DebugCategory::INSPECTOR_PROFILER, "EndStartedProfilers\n"); - V8ProfilerConnection* connection = env->coverage_connection(); + V8ProfilerConnection* connection = env->cpu_profiler_connection(); if (connection != nullptr && !connection->ending()) { - Debug( - env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n"); + Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n"); connection->End(); } - connection = env->cpu_profiler_connection(); + connection = env->coverage_connection(); if (connection != nullptr && !connection->ending()) { - Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n"); + Debug( + env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n"); connection->End(); } } -- cgit v1.2.3