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>
This commit is contained in:
Joyee Cheung 2019-03-23 07:39:52 +08:00
parent baa54a5ae7
commit 864860e9f3
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
20 changed files with 388 additions and 265 deletions

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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
};

View File

@ -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',

View File

@ -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_;
}

View File

@ -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()) {

View File

@ -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

View File

@ -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',

View File

@ -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) {}
V8ProfilerConnection::V8ProfilerConnection(Environment* env)
: session_(env->inspector_agent()->Connect(
std::make_unique<V8ProfilerConnection::V8ProfilerSessionDelegate>(
this),
false)),
env_(env) {}
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");
}
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 DispatchMessage(Isolate* isolate, Local<String> message) {
session_->Dispatch(ToProtocolString(isolate, message)->string());
}
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 Start() {
SetConnection(object());
StartProfiling();
}
void End(Local<Function> callback) {
SetMessageCallback(callback);
EndProfiling();
}
// 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();
}
void SetMessageCallback(Local<Function> callback) override {
return env()->set_on_coverage_message_function(callback);
}
static V8ProfilerConnection* GetConnection(Environment* env) {
return Unwrap<V8CoverageConnection>(env->coverage_connection());
}
void SetConnection(Local<Object> obj) override {
env()->set_coverage_connection(obj);
}
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);
}
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);
}
private:
std::unique_ptr<inspector::InspectorSession> session_;
};
void StartCoverageCollection(Environment* env) {
V8CoverageConnection* connection = new V8CoverageConnection(env);
connection->Start();
void V8ProfilerConnection::DispatchMessage(Local<String> message) {
session_->Dispatch(ToProtocolString(env()->isolate(), message)->string());
}
static void EndCoverageCollection(const FunctionCallbackInfo<Value>& args) {
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;
}
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;
}
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);
}
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;
}
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());
}
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>();
}
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>();
}
if (result_v->IsUndefined()) {
fprintf(stderr, "'result' from coverage message is undefined\n");
return MaybeLocal<String>();
}
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>();
}
return result_s;
}
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) {
CHECK_NULL(env->coverage_connection());
env->set_coverage_connection(std::make_unique<V8CoverageConnection>(env));
env->coverage_connection()->Start();
}
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

75
src/inspector_profiler.h Normal file
View File

@ -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_

View File

@ -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
}

View File

@ -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));

View File

@ -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

View File

@ -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);

View File

@ -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();

View File

@ -25,8 +25,6 @@ using v8::Value;
extern const uint64_t timeOrigin;
double GetCurrentTimeInMicroseconds();
static inline const char* GetPerformanceMilestoneName(
enum PerformanceMilestone milestone) {
switch (milestone) {

View File

@ -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);
}

View File

@ -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_);
}

View File

@ -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);

View File

@ -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) => {