summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/env-inl.h20
-rw-r--r--src/env.cc1
-rw-r--r--src/env.h23
-rw-r--r--src/inspector/node_inspector.gypi1
-rw-r--r--src/inspector_profiler.cc301
-rw-r--r--src/inspector_profiler.h75
-rw-r--r--src/node.cc10
-rw-r--r--src/node_file.cc7
-rw-r--r--src/node_file.h5
-rw-r--r--src/node_internals.h11
-rw-r--r--src/node_perf.cc23
-rw-r--r--src/node_perf.h2
-rw-r--r--src/node_process_methods.cc6
-rw-r--r--src/node_worker.cc3
-rw-r--r--src/util.cc59
15 files changed, 365 insertions, 182 deletions
diff --git a/src/env-inl.h b/src/env-inl.h
index ef054be4cb..a489a4001a 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -638,6 +638,26 @@ inline const std::vector<std::string>& 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<profiler::V8CoverageConnection> 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<HostPort> Environment::inspector_host_port() {
return inspector_host_port_;
}
diff --git a/src/env.cc b/src/env.cc
index 1542638e77..3d85bcfcf7 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -715,7 +715,6 @@ Local<Value> Environment::GetNow() {
return Number::New(isolate(), static_cast<double>(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 9d28907212..82d9943198 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<profiler::V8CoverageConnection> 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<double> destroy_async_id_list_;
+#if HAVE_INSPECTOR
+ std::unique_ptr<profiler::V8CoverageConnection> coverage_connection_;
+ std::string coverage_directory_;
+#endif // HAVE_INSPECTOR
+
std::shared_ptr<EnvironmentOptions> 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 d381eb9133..2eabd9dd31 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 942a1d148a..27b1db14c9 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
diff --git a/src/inspector_profiler.h b/src/inspector_profiler.h
new file mode 100644
index 0000000000..43a8d54163
--- /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<v8::String> message);
+ // Write the result to a path
+ bool WriteResult(const char* path, v8::Local<v8::String> result);
+
+ private:
+ std::unique_ptr<inspector::InspectorSession> 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<v8::String> message);
+ v8::MaybeLocal<v8::String> GetResult(v8::Local<v8::String> message);
+ std::unique_ptr<inspector::InspectorSession> 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 3401ce30b2..6e2327ef51 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<Value> RunBootstrapping(Environment* env) {
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
- std::string coverage;
- bool rc = credentials::SafeGetenv("NODE_V8_COVERAGE", &coverage);
- if (rc && !coverage.empty()) {
+ Local<String> 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 ec0a00e956..6cb3c9499f 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -1235,8 +1235,11 @@ static void RMDir(const FunctionCallbackInfo<Value>& 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 746967f364..2c87f5e44d 100644
--- a/src/node_file.h
+++ b/src/node_file.h
@@ -467,6 +467,11 @@ class FileHandle : public AsyncWrap, public StreamBase {
std::unique_ptr<FileHandleReadWrap> 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 e299d87730..7452dc05e9 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -308,15 +308,26 @@ v8::MaybeLocal<v8::Value> ExecuteBootstrapper(
std::vector<v8::Local<v8::String>>* parameters,
std::vector<v8::Local<v8::Value>>* arguments);
void MarkBootstrapComplete(const v8::FunctionCallbackInfo<v8::Value>& 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<v8::String> string);
+
class DiagnosticFilename {
public:
static void LocalTime(TIME_TYPE* tm_struct);
diff --git a/src/node_perf.cc b/src/node_perf.cc
index 1ca5cd5e01..393c4747f9 100644
--- a/src/node_perf.cc
+++ b/src/node_perf.cc
@@ -6,10 +6,6 @@
#include <cinttypes>
-#ifdef __POSIX__
-#include <sys/time.h> // 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<uint64_t>(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<Object> obj) {
Environment* env = entry.env();
diff --git a/src/node_perf.h b/src/node_perf.h
index a8e43dc347..2e77c6f25e 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 dd27329ee2..e53a5a7015 100644
--- a/src/node_process_methods.cc
+++ b/src/node_process_methods.cc
@@ -171,6 +171,12 @@ static void Kill(const FunctionCallbackInfo<Value>& 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 c93584c68c..7de9c355f6 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 099911cf25..49c8a0f46a 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -28,7 +28,14 @@
#include "uv.h"
#ifdef _WIN32
+#include <io.h> // _S_IREAD _S_IWRITE
#include <time.h>
+#ifndef S_IRUSR
+#define S_IRUSR _S_IREAD
+#endif // S_IRUSR
+#ifndef S_IWUSR
+#define S_IWUSR _S_IWRITE
+#endif // S_IWUSR
#else
#include <sys/time.h>
#include <sys/types.h>
@@ -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<uint64_t>(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<v8::String> 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);