Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 36.4k
cli: implement --cpu-prof[-path]#27147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -23,10 +23,14 @@ using v8::Value; | ||
| using v8_inspector::StringBuffer; | ||
| using v8_inspector::StringView; | ||
| #ifdef __POSIX__ | ||
| const char* const kPathSeparator = "/"; | ||
| #else | ||
| #ifdef _WIN32 | ||
| const char* const kPathSeparator = "\\/"; | ||
| /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ | ||
| #define CWD_BUFSIZE (MAX_PATH * 4) | ||
| #else | ||
| #include <climits> // PATH_MAX on Solaris. | ||
| const char* const kPathSeparator = "/"; | ||
| #define CWD_BUFSIZE (PATH_MAX) | ||
| #endif | ||
| std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate, | ||
| @@ -180,6 +184,116 @@ 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> context = env()->context(); | ||
| Context::Scope context_scope(context); | ||
| Local<String> result; | ||
| if (!String::NewFromTwoByte(isolate, | ||
| message.characters16(), | ||
| NewStringType::kNormal, | ||
| message.length()) | ||
| .ToLocal(&result)) { | ||
| fprintf(stderr, "Failed to convert profiling message\n"); | ||
| } | ||
| WriteCpuProfile(result); | ||
| } | ||
| void V8CpuProfilerConnection::WriteCpuProfile(Local<String> message) { | ||
| const std::string& path = env()->cpu_profile_path(); | ||
| CHECK(!path.empty()); | ||
| std::string directory = path.substr(0, path.find_last_of(kPathSeparator)); | ||
| if (directory != path) { | ||
| 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<String> result = GetResult(message); | ||
| if (!result.IsEmpty()) { | ||
| WriteResult(path.c_str(), result.ToLocalChecked()); | ||
| } | ||
| } | ||
| MaybeLocal<String> V8CpuProfilerConnection::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 CPU profile 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 CPU profile message\n"); | ||
| return MaybeLocal<String>(); | ||
| } | ||
| if (!result_v->IsObject()) { | ||
| fprintf(stderr, "'result' from CPU profile message is not an object\n"); | ||
| return MaybeLocal<String>(); | ||
| } | ||
| Local<Value> profile_v; | ||
| if (!result_v.As<Object>() | ||
| ->Get(context, FIXED_ONE_BYTE_STRING(isolate, "profile")) | ||
| .ToLocal(&profile_v)) { | ||
| fprintf(stderr, "'profile' from CPU profile result is undefined\n"); | ||
| return MaybeLocal<String>(); | ||
| } | ||
| Local<String> result_s; | ||
| if (!v8::JSON::Stringify(context, profile_v).ToLocal(&result_s)) { | ||
| fprintf(stderr, "Failed to stringify CPU profile result\n"); | ||
| return MaybeLocal<String>(); | ||
| } | ||
| return result_s; | ||
| } | ||
| void V8CpuProfilerConnection::Start() { | ||
| Debug(env(), DebugCategory::INSPECTOR_PROFILER, "Sending Profiler.start\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.start"})"); | ||
| DispatchMessage(enable); | ||
| DispatchMessage(start); | ||
| } | ||
| void V8CpuProfilerConnection::End() { | ||
| CHECK_EQ(ending_, false); | ||
| ending_ = true; | ||
| Debug(env(), DebugCategory::INSPECTOR_PROFILER, "Sending Profiler.stop\n"); | ||
| Isolate* isolate = env()->isolate(); | ||
| HandleScope scope(isolate); | ||
| Local<String> end = | ||
| FIXED_ONE_BYTE_STRING(isolate, R"({"id": 3, "method": "Profiler.stop"})"); | ||
| DispatchMessage(end); | ||
| } | ||
| // For now, we only support coverage profiling, but we may add more | ||
| // in the future. | ||
| void EndStartedProfilers(Environment* env) { | ||
| @@ -190,6 +304,12 @@ void EndStartedProfilers(Environment* env) { | ||
| env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n"); | ||
| connection->End(); | ||
| } | ||
| connection = env->cpu_profiler_connection(); | ||
| if (connection != nullptr && !connection->ending()) { | ||
| Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending cpu profiling\n"); | ||
| connection->End(); | ||
| } | ||
| } | ||
| void StartCoverageCollection(Environment* env) { | ||
| @@ -198,6 +318,26 @@ void StartCoverageCollection(Environment* env) { | ||
| env->coverage_connection()->Start(); | ||
| } | ||
| void StartCpuProfiling(Environment* env, const std::string& profile_path) { | ||
| std::string path; | ||
| if (profile_path.empty()) { | ||
| char cwd[CWD_BUFSIZE]; | ||
| size_t size = CWD_BUFSIZE; | ||
| int err = uv_cwd(cwd, &size); | ||
| // TODO(joyeecheung): fallback to exec path / argv[0] | ||
| CHECK_EQ(err, 0); | ||
| CHECK_GT(size, 0); | ||
| DiagnosticFilename filename(env, "CPU", "cpuprofile"); | ||
| ||
| path = cwd + std::string(kPathSeparator) + (*filename); | ||
| } else { | ||
| path = profile_path; | ||
| } | ||
| env->set_cpu_profile_path(std::move(path)); | ||
| env->set_cpu_profiler_connection( | ||
| std::make_unique<V8CpuProfilerConnection>(env)); | ||
| env->cpu_profiler_connection()->Start(); | ||
| } | ||
| static void SetCoverageDirectory(const FunctionCallbackInfo<Value>& args) { | ||
| CHECK(args[0]->IsString()); | ||
| Environment* env = Environment::GetCurrent(args); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| 'use strict'; | ||
| function fib(n) { | ||
| if (n === 0 || n === 1) return n; | ||
| return fib(n - 1) + fib(n - 2); | ||
| } | ||
| fib(parseInt(process.argv[2]) || 35); | ||
| process.exit(55); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| 'use strict'; | ||
| function fib(n) { | ||
| if (n === 0 || n === 1) return n; | ||
| return fib(n - 1) + fib(n - 2); | ||
| } | ||
| fib(parseInt(process.argv[2]) || 35); | ||
| process.kill(process.pid, "SIGINT"); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| 'use strict'; | ||
| const{ Worker }=require('worker_threads'); | ||
| constpath=require('path'); | ||
| newWorker(path.join(__dirname,'fibonacci.js'),{ | ||
| execArgv: ['--cpu-prof'] | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| 'use strict'; | ||
| function fib(n) { | ||
| if (n === 0 || n === 1) return n; | ||
| return fib(n - 1) + fib(n - 2); | ||
| } | ||
| const n = parseInt(process.env.FIB) || 35; | ||
| process.stdout.write(`${fib(n)}\n`); |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.