From 864860e9f3d4eed0b0b81af55197d7e525ea6306 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 23 Mar 2019 07:39:52 +0800 Subject: [PATCH] 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 --- lib/internal/bootstrap/pre_execution.js | 12 +- lib/internal/process/per_thread.js | 4 - lib/internal/profiler.js | 45 ---- node.gyp | 1 - src/env-inl.h | 20 ++ src/env.cc | 1 - src/env.h | 23 +- src/inspector/node_inspector.gypi | 1 + src/inspector_profiler.cc | 301 ++++++++++++------------ src/inspector_profiler.h | 75 ++++++ src/node.cc | 10 +- src/node_file.cc | 7 +- src/node_file.h | 5 + src/node_internals.h | 11 + src/node_perf.cc | 23 -- src/node_perf.h | 2 - src/node_process_methods.cc | 6 + src/node_worker.cc | 3 + src/util.cc | 59 +++++ test/parallel/test-bootstrap-modules.js | 2 +- 20 files changed, 367 insertions(+), 244 deletions(-) delete mode 100644 lib/internal/profiler.js create mode 100644 src/inspector_profiler.h diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 297aea9dd77640..c1c58a81ff36b5 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -110,20 +110,10 @@ function setupWarningHandler() { // Setup User-facing NODE_V8_COVERAGE environment variable that writes // ScriptCoverage to a specified file. function setupCoverageHooks(dir) { - const originalReallyExit = process.reallyExit; const cwd = require('internal/process/execution').tryGetCwd(); const { resolve } = require('path'); const coverageDirectory = resolve(cwd, dir); - const { - writeCoverage, - setCoverageDirectory - } = require('internal/profiler'); - setCoverageDirectory(coverageDirectory); - process.on('exit', writeCoverage); - process.reallyExit = (code) => { - writeCoverage(); - originalReallyExit(code); - }; + internalBinding('profiler').setCoverageDirectory(coverageDirectory); return coverageDirectory; } diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 7d2e83100c7939..aa2bcce16c3871 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -157,10 +157,6 @@ function wrapProcessMethods(binding) { function kill(pid, sig) { var err; - if (process.env.NODE_V8_COVERAGE) { - const { writeCoverage } = require('internal/profiler'); - writeCoverage(); - } // eslint-disable-next-line eqeqeq if (pid != (pid | 0)) { diff --git a/lib/internal/profiler.js b/lib/internal/profiler.js deleted file mode 100644 index 5e8e13038be0f2..00000000000000 --- a/lib/internal/profiler.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -// Implements coverage collection exposed by the `NODE_V8_COVERAGE` -// environment variable which can also be used in the user land. - -const { JSON } = primordials; - -let coverageDirectory; - -function writeCoverage() { - const { join } = require('path'); - const { mkdirSync, writeFileSync } = require('fs'); - const { threadId } = require('internal/worker'); - - const filename = `coverage-${process.pid}-${Date.now()}-${threadId}.json`; - try { - mkdirSync(coverageDirectory, { recursive: true }); - } catch (err) { - if (err.code !== 'EEXIST') { - console.error(err); - return; - } - } - - const target = join(coverageDirectory, filename); - internalBinding('profiler').endCoverage((msg) => { - try { - const coverageInfo = JSON.parse(msg).result; - if (coverageInfo) { - writeFileSync(target, JSON.stringify(coverageInfo)); - } - } catch (err) { - console.error(err); - } - }); -} - -function setCoverageDirectory(dir) { - coverageDirectory = dir; -} - -module.exports = { - writeCoverage, - setCoverageDirectory -}; diff --git a/node.gyp b/node.gyp index e0aa84bf3c7491..676be6e038b62f 100644 --- a/node.gyp +++ b/node.gyp @@ -169,7 +169,6 @@ 'lib/internal/process/worker_thread_only.js', 'lib/internal/process/report.js', 'lib/internal/process/task_queues.js', - 'lib/internal/profiler.js', 'lib/internal/querystring.js', 'lib/internal/readline.js', 'lib/internal/repl.js', diff --git a/src/env-inl.h b/src/env-inl.h index ef054be4cb8bf8..a489a4001a2557 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -638,6 +638,26 @@ inline const std::vector& Environment::exec_argv() { return exec_argv_; } +#if HAVE_INSPECTOR +inline void Environment::set_coverage_directory(const char* dir) { + coverage_directory_ = std::string(dir); +} + +inline void Environment::set_coverage_connection( + std::unique_ptr connection) { + CHECK_NULL(coverage_connection_); + std::swap(coverage_connection_, connection); +} + +inline profiler::V8CoverageConnection* Environment::coverage_connection() { + return coverage_connection_.get(); +} + +inline const std::string& Environment::coverage_directory() const { + return coverage_directory_; +} +#endif // HAVE_INSPECTOR + inline std::shared_ptr Environment::inspector_host_port() { return inspector_host_port_; } diff --git a/src/env.cc b/src/env.cc index 1542638e77d509..3d85bcfcf703dc 100644 --- a/src/env.cc +++ b/src/env.cc @@ -715,7 +715,6 @@ Local Environment::GetNow() { return Number::New(isolate(), static_cast(now)); } - void Environment::set_debug_categories(const std::string& cats, bool enabled) { std::string debug_categories = cats; while (!debug_categories.empty()) { diff --git a/src/env.h b/src/env.h index 9d28907212ac6f..82d9943198fc06 100644 --- a/src/env.h +++ b/src/env.h @@ -27,6 +27,7 @@ #include "aliased_buffer.h" #if HAVE_INSPECTOR #include "inspector_agent.h" +#include "inspector_profiler.h" #endif #include "handle_wrap.h" #include "node.h" @@ -67,6 +68,12 @@ namespace tracing { class AgentWriterHandle; } +#if HAVE_INSPECTOR +namespace profiler { +class V8CoverageConnection; +} // namespace profiler +#endif // HAVE_INSPECTOR + namespace worker { class Worker; } @@ -366,7 +373,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; V(async_hooks_init_function, v8::Function) \ V(async_hooks_promise_resolve_function, v8::Function) \ V(buffer_prototype_object, v8::Object) \ - V(coverage_connection, v8::Object) \ V(crypto_key_object_constructor, v8::Function) \ V(domain_callback, v8::Function) \ V(domexception_function, v8::Function) \ @@ -390,7 +396,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; V(inspector_console_extension_installer, v8::Function) \ V(message_port, v8::Object) \ V(native_module_require, v8::Function) \ - V(on_coverage_message_function, v8::Function) \ V(performance_entry_callback, v8::Function) \ V(performance_entry_template, v8::Function) \ V(process_object, v8::Object) \ @@ -1116,6 +1121,15 @@ class Environment : public MemoryRetainer { inline AsyncRequest* thread_stopper() { return &thread_stopper_; } +#if HAVE_INSPECTOR + void set_coverage_connection( + std::unique_ptr connection); + profiler::V8CoverageConnection* coverage_connection(); + + inline void set_coverage_directory(const char* directory); + inline const std::string& coverage_directory() const; +#endif // HAVE_INSPECTOR + private: inline void CreateImmediate(native_immediate_callback cb, void* data, @@ -1146,6 +1160,11 @@ class Environment : public MemoryRetainer { size_t async_callback_scope_depth_ = 0; std::vector destroy_async_id_list_; +#if HAVE_INSPECTOR + std::unique_ptr coverage_connection_; + std::string coverage_directory_; +#endif // HAVE_INSPECTOR + std::shared_ptr options_; // options_ contains debug options parsed from CLI arguments, // while inspector_host_port_ stores the actual inspector host diff --git a/src/inspector/node_inspector.gypi b/src/inspector/node_inspector.gypi index d381eb91339362..2eabd9dd3105fa 100644 --- a/src/inspector/node_inspector.gypi +++ b/src/inspector/node_inspector.gypi @@ -44,6 +44,7 @@ '../../src/inspector_io.cc', '../../src/inspector_agent.h', '../../src/inspector_io.h', + '../../src/inspector_profiler.h', '../../src/inspector_profiler.cc', '../../src/inspector_js_api.cc', '../../src/inspector_socket.cc', diff --git a/src/inspector_profiler.cc b/src/inspector_profiler.cc index 942a1d148a2f79..27b1db14c92bc0 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 ToProtocolString(Isolate* isolate, Local 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 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 v8string = - String::NewFromTwoByte(isolate, - message.characters16(), - NewStringType::kNormal, - message.length()); - Local args[] = {v8string.ToLocalChecked().As()}; - 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( + this), + false)), + env_(env) {} - explicit V8ProfilerConnection(Environment* env, Local obj) - : BaseObject(env, obj), session_(nullptr) { - inspector::Agent* inspector = env->inspector_agent(); - std::unique_ptr session = inspector->Connect( - std::make_unique(this), false); - session_ = std::move(session); - MakeWeak(); - } +void V8ProfilerConnection::DispatchMessage(Local message) { + session_->Dispatch(ToProtocolString(env()->isolate(), message)->string()); +} - void DispatchMessage(Isolate* isolate, Local message) { - session_->Dispatch(ToProtocolString(isolate, message)->string()); +bool V8ProfilerConnection::WriteResult(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; } + Debug( + env(), DebugCategory::INSPECTOR_PROFILER, "Written result to %s\n", path); + return true; +} - static MaybeLocal CreateConnectionObject(Environment* env) { - Isolate* isolate = env->isolate(); - Local context = env->context(); - EscapableHandleScope scope(isolate); - - Local t = ObjectTemplate::New(isolate); - t->SetInternalFieldCount(1); - Local obj; - if (!t->NewInstance(context).ToLocal(&obj)) { - return MaybeLocal(); - } - - 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 = env()->context(); + HandleScope handle_scope(isolate); + Context::Scope context_scope(context); + Local 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 callback) { - SetMessageCallback(callback); - EndProfiling(); +bool V8CoverageConnection::WriteCoverage(Local 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 GetMessageCallback() = 0; - virtual void SetMessageCallback(Local callback) = 0; - // Use DispatchMessage() to dispatch necessary inspector messages - virtual void StartProfiling() = 0; - virtual void EndProfiling() = 0; - virtual void SetConnection(Local connection) = 0; - - private: - std::unique_ptr session_; -}; - -class V8CoverageConnection : public V8ProfilerConnection { - public: - explicit V8CoverageConnection(Environment* env) - : V8ProfilerConnection(env, - CreateConnectionObject(env).ToLocalChecked()) {} - - Local 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(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 result = GetResult(message); + if (result.IsEmpty()) { + return false; } + return WriteResult(target.c_str(), result.ToLocalChecked()); +} - void SetMessageCallback(Local callback) override { - return env()->set_on_coverage_message_function(callback); +MaybeLocal V8CoverageConnection::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 coverage result as JSON object\n"); + return MaybeLocal(); } - static V8ProfilerConnection* GetConnection(Environment* env) { - return Unwrap(env->coverage_connection()); + 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(); } - void SetConnection(Local obj) override { - env()->set_coverage_connection(obj); + if (result_v->IsUndefined()) { + fprintf(stderr, "'result' from coverage message is undefined\n"); + return MaybeLocal(); } - void StartProfiling() override { - Debug(env(), - DebugCategory::INSPECTOR_PROFILER, - "Sending Profiler.startPreciseCoverage\n"); - Isolate* isolate = env()->isolate(); - Local enable = FIXED_ONE_BYTE_STRING( - isolate, "{\"id\": 1, \"method\": \"Profiler.enable\"}"); - Local start = FIXED_ONE_BYTE_STRING( - isolate, - "{" - "\"id\": 2," - "\"method\": \"Profiler.startPreciseCoverage\"," - "\"params\": {\"callCount\": true, \"detailed\": true}" - "}"); - DispatchMessage(isolate, enable); - DispatchMessage(isolate, start); + Local result_s; + if (!v8::JSON::Stringify(context, result_v).ToLocal(&result_s)) { + fprintf(stderr, "Failed to stringify coverage result\n"); + return MaybeLocal(); } - void EndProfiling() override { - Debug(env(), - DebugCategory::INSPECTOR_PROFILER, - "Sending Profiler.takePreciseCoverage\n"); - Isolate* isolate = env()->isolate(); - Local end = - FIXED_ONE_BYTE_STRING(isolate, - "{" - "\"id\": 3," - "\"method\": \"Profiler.takePreciseCoverage\"" - "}"); - DispatchMessage(isolate, end); - } + return result_s; +} - private: - std::unique_ptr session_; -}; +void V8CoverageConnection::Start() { + Debug(env(), + DebugCategory::INSPECTOR_PROFILER, + "Sending Profiler.startPreciseCoverage\n"); + Isolate* isolate = env()->isolate(); + Local enable = FIXED_ONE_BYTE_STRING( + isolate, R"({"id": 1, "method": "Profiler.enable"})"); + Local 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 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(env)); + env->coverage_connection()->Start(); } -static void EndCoverageCollection(const FunctionCallbackInfo& args) { +static void SetCoverageDirectory(const FunctionCallbackInfo& 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()); + node::Utf8Value directory(env->isolate(), args[0].As()); + env->set_coverage_directory(*directory); } static void Initialize(Local target, @@ -206,8 +210,9 @@ static void Initialize(Local target, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); - env->SetMethod(target, "endCoverage", EndCoverageCollection); + env->SetMethod(target, "setCoverageDirectory", SetCoverageDirectory); } + } // namespace profiler } // namespace node diff --git a/src/inspector_profiler.h b/src/inspector_profiler.h new file mode 100644 index 00000000000000..43a8d541638015 --- /dev/null +++ b/src/inspector_profiler.h @@ -0,0 +1,75 @@ +#ifndef SRC_INSPECTOR_PROFILER_H_ +#define SRC_INSPECTOR_PROFILER_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#if !HAVE_INSPECTOR +#error("This header can only be used when inspector is enabled") +#endif + +#include "env.h" +#include "inspector_agent.h" + +namespace node { +// Forward declaration to break recursive dependency chain with src/env.h. +class Environment; + +namespace profiler { + +class V8ProfilerConnection { + public: + class V8ProfilerSessionDelegate : public inspector::InspectorSessionDelegate { + public: + explicit V8ProfilerSessionDelegate(V8ProfilerConnection* connection) + : connection_(connection) {} + + void SendMessageToFrontend( + const v8_inspector::StringView& message) override { + connection_->OnMessage(message); + } + + private: + V8ProfilerConnection* connection_; + }; + + explicit V8ProfilerConnection(Environment* env); + virtual ~V8ProfilerConnection() = default; + Environment* env() { return env_; } + + // Use DispatchMessage() to dispatch necessary inspector messages + virtual void Start() = 0; + virtual void End() = 0; + // Override this to respond to the messages sent from the session. + virtual void OnMessage(const v8_inspector::StringView& message) = 0; + virtual bool ending() const = 0; + + void DispatchMessage(v8::Local message); + // Write the result to a path + bool WriteResult(const char* path, v8::Local result); + + private: + std::unique_ptr session_; + Environment* env_ = nullptr; +}; + +class V8CoverageConnection : public V8ProfilerConnection { + public: + explicit V8CoverageConnection(Environment* env) : V8ProfilerConnection(env) {} + + void Start() override; + void End() override; + void OnMessage(const v8_inspector::StringView& message) override; + bool ending() const override { return ending_; } + + private: + bool WriteCoverage(v8::Local message); + v8::MaybeLocal GetResult(v8::Local message); + std::unique_ptr session_; + bool ending_ = false; +}; + +} // namespace profiler +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#endif // SRC_INSPECTOR_PROFILER_H_ diff --git a/src/node.cc b/src/node.cc index 3401ce30b2d22b..6e2327ef5180bb 100644 --- a/src/node.cc +++ b/src/node.cc @@ -170,6 +170,8 @@ static const unsigned kMaxSignal = 32; void WaitForInspectorDisconnect(Environment* env) { #if HAVE_INSPECTOR + profiler::EndStartedProfilers(env); + if (env->inspector_agent()->IsActive()) { // Restore signal dispositions, the app is done and is no longer // capable of handling signals. @@ -240,13 +242,13 @@ MaybeLocal RunBootstrapping(Environment* env) { Isolate* isolate = env->isolate(); Local context = env->context(); - std::string coverage; - bool rc = credentials::SafeGetenv("NODE_V8_COVERAGE", &coverage); - if (rc && !coverage.empty()) { + Local coverage_str = env->env_vars()->Get( + isolate, FIXED_ONE_BYTE_STRING(isolate, "NODE_V8_COVERAGE")); + if (!coverage_str.IsEmpty() && coverage_str->Length() > 0) { #if HAVE_INSPECTOR profiler::StartCoverageCollection(env); #else - fprintf(stderr, "NODE_V8_COVERAGE cannot be used without inspector"); + fprintf(stderr, "NODE_V8_COVERAGE cannot be used without inspector\n"); #endif // HAVE_INSPECTOR } diff --git a/src/node_file.cc b/src/node_file.cc index ec0a00e9561a3d..6cb3c9499f2795 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1235,8 +1235,11 @@ static void RMDir(const FunctionCallbackInfo& args) { } } -int MKDirpSync(uv_loop_t* loop, uv_fs_t* req, const std::string& path, int mode, - uv_fs_cb cb = nullptr) { +int MKDirpSync(uv_loop_t* loop, + uv_fs_t* req, + const std::string& path, + int mode, + uv_fs_cb cb) { FSContinuationData continuation_data(req, mode, cb); continuation_data.PushPath(std::move(path)); diff --git a/src/node_file.h b/src/node_file.h index 746967f364c1e6..2c87f5e44d0d90 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -467,6 +467,11 @@ class FileHandle : public AsyncWrap, public StreamBase { std::unique_ptr current_read_ = nullptr; }; +int MKDirpSync(uv_loop_t* loop, + uv_fs_t* req, + const std::string& path, + int mode, + uv_fs_cb cb = nullptr); } // namespace fs } // namespace node diff --git a/src/node_internals.h b/src/node_internals.h index e299d8773079ec..7452dc05e981d7 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -308,15 +308,26 @@ v8::MaybeLocal ExecuteBootstrapper( std::vector>* parameters, std::vector>* arguments); void MarkBootstrapComplete(const v8::FunctionCallbackInfo& args); + +#if HAVE_INSPECTOR namespace profiler { void StartCoverageCollection(Environment* env); +void EndStartedProfilers(Environment* env); } +#endif // HAVE_INSPECTOR + #ifdef _WIN32 typedef SYSTEMTIME TIME_TYPE; #else // UNIX, OSX typedef struct tm TIME_TYPE; #endif +double GetCurrentTimeInMicroseconds(); +int WriteFileSync(const char* path, uv_buf_t buf); +int WriteFileSync(v8::Isolate* isolate, + const char* path, + v8::Local string); + class DiagnosticFilename { public: static void LocalTime(TIME_TYPE* tm_struct); diff --git a/src/node_perf.cc b/src/node_perf.cc index 1ca5cd5e01ea47..393c4747f9a883 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -6,10 +6,6 @@ #include -#ifdef __POSIX__ -#include // gettimeofday -#endif - namespace node { namespace performance { @@ -37,8 +33,6 @@ using v8::String; using v8::Uint32Array; using v8::Value; -// Microseconds in a second, as a float. -#define MICROS_PER_SEC 1e6 // Microseconds in a millisecond, as a float. #define MICROS_PER_MILLIS 1e3 @@ -57,23 +51,6 @@ void performance_state::Mark(enum PerformanceMilestone milestone, TRACE_EVENT_SCOPE_THREAD, ts / 1000); } -double GetCurrentTimeInMicroseconds() { -#ifdef _WIN32 -// The difference between the Unix Epoch and the Windows Epoch in 100-ns ticks. -#define TICKS_TO_UNIX_EPOCH 116444736000000000LL - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - uint64_t filetime_int = static_cast(ft.dwHighDateTime) << 32 | - ft.dwLowDateTime; - // FILETIME is measured in terms of 100 ns. Convert that to 1 us (1000 ns). - return (filetime_int - TICKS_TO_UNIX_EPOCH) / 10.; -#else - struct timeval tp; - gettimeofday(&tp, nullptr); - return MICROS_PER_SEC * tp.tv_sec + tp.tv_usec; -#endif -} - // Initialize the performance entry object properties inline void InitObject(const PerformanceEntry& entry, Local obj) { Environment* env = entry.env(); diff --git a/src/node_perf.h b/src/node_perf.h index a8e43dc3476cc2..2e77c6f25ef744 100644 --- a/src/node_perf.h +++ b/src/node_perf.h @@ -25,8 +25,6 @@ using v8::Value; extern const uint64_t timeOrigin; -double GetCurrentTimeInMicroseconds(); - static inline const char* GetPerformanceMilestoneName( enum PerformanceMilestone milestone) { switch (milestone) { diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index dd27329ee2bcf4..e53a5a7015c8e3 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -171,6 +171,12 @@ static void Kill(const FunctionCallbackInfo& args) { if (!args[0]->Int32Value(context).To(&pid)) return; int sig; if (!args[1]->Int32Value(context).To(&sig)) return; + // TODO(joyeecheung): white list the signals? + +#if HAVE_INSPECTOR + profiler::EndStartedProfilers(env); +#endif + int err = uv_kill(pid, sig); args.GetReturnValue().Set(err); } diff --git a/src/node_worker.cc b/src/node_worker.cc index c93584c68c8f6f..7de9c355f65b6b 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -329,6 +329,9 @@ void Worker::Run() { if (exit_code_ == 0 && !stopped) exit_code_ = exit_code; +#if HAVE_INSPECTOR + profiler::EndStartedProfilers(env_.get()); +#endif Debug(this, "Exiting thread for worker %llu with exit code %d", thread_id_, exit_code_); } diff --git a/src/util.cc b/src/util.cc index 099911cf25d38a..49c8a0f46a92eb 100644 --- a/src/util.cc +++ b/src/util.cc @@ -28,7 +28,14 @@ #include "uv.h" #ifdef _WIN32 +#include // _S_IREAD _S_IWRITE #include +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif // S_IRUSR +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif // S_IWUSR #else #include #include @@ -40,6 +47,9 @@ namespace node { +// Microseconds in a second, as a float. +#define MICROS_PER_SEC 1e6 + using v8::ArrayBufferView; using v8::Isolate; using v8::Local; @@ -152,6 +162,55 @@ void ThrowErrStringTooLong(Isolate* isolate) { isolate->ThrowException(ERR_STRING_TOO_LONG(isolate)); } +double GetCurrentTimeInMicroseconds() { +#ifdef _WIN32 +// The difference between the Unix Epoch and the Windows Epoch in 100-ns ticks. +#define TICKS_TO_UNIX_EPOCH 116444736000000000LL + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t filetime_int = + static_cast(ft.dwHighDateTime) << 32 | ft.dwLowDateTime; + // FILETIME is measured in terms of 100 ns. Convert that to 1 us (1000 ns). + return (filetime_int - TICKS_TO_UNIX_EPOCH) / 10.; +#else + struct timeval tp; + gettimeofday(&tp, nullptr); + return MICROS_PER_SEC * tp.tv_sec + tp.tv_usec; +#endif +} + +int WriteFileSync(const char* path, uv_buf_t buf) { + uv_fs_t req; + int fd = uv_fs_open(nullptr, + &req, + path, + O_WRONLY | O_CREAT | O_TRUNC, + S_IWUSR | S_IRUSR, + nullptr); + uv_fs_req_cleanup(&req); + if (fd < 0) { + return fd; + } + + int err = uv_fs_write(nullptr, &req, fd, &buf, 1, 0, nullptr); + uv_fs_req_cleanup(&req); + if (err < 0) { + return err; + } + + err = uv_fs_close(nullptr, &req, fd, nullptr); + uv_fs_req_cleanup(&req); + return err; +} + +int WriteFileSync(v8::Isolate* isolate, + const char* path, + v8::Local string) { + node::Utf8Value utf8(isolate, string); + uv_buf_t buf = uv_buf_init(utf8.out(), utf8.length()); + return WriteFileSync(path, buf); +} + void DiagnosticFilename::LocalTime(TIME_TYPE* tm_struct) { #ifdef _WIN32 GetLocalTime(tm_struct); diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 4693072b8159ad..50b0a53724da94 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -106,7 +106,7 @@ if (process.features.inspector) { } if (process.env.NODE_V8_COVERAGE) { - expectedModules.add('NativeModule internal/profiler'); + expectedModules.add('Internal Binding profiler'); } const difference = (setA, setB) => {