@@ -23,10 +23,14 @@ using v8::Value;
2323using v8_inspector::StringBuffer;
2424using v8_inspector::StringView;
2525
26- #ifdef __POSIX__
27- const char * const kPathSeparator = " /" ;
28- #else
26+ #ifdef _WIN32
2927const char * const kPathSeparator = " \\ /" ;
28+ /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
29+ #define CWD_BUFSIZE (MAX_PATH * 4 )
30+ #else
31+ #include < climits> // PATH_MAX on Solaris.
32+ const char * const kPathSeparator = " /" ;
33+ #define CWD_BUFSIZE (PATH_MAX)
3034#endif
3135
3236std::unique_ptr<StringBuffer> ToProtocolString (Isolate* isolate,
@@ -180,6 +184,116 @@ void V8CoverageConnection::End() {
180184 DispatchMessage (end);
181185}
182186
187+ void V8CpuProfilerConnection::OnMessage (
188+ const v8_inspector::StringView& message) {
189+ Debug (env (),
190+ DebugCategory::INSPECTOR_PROFILER,
191+ " Receive cpu profiling message, ending = %s\n " ,
192+ ending_ ? " true" : " false" );
193+ if (!ending_) {
194+ return ;
195+ }
196+ Isolate* isolate = env ()->isolate ();
197+ HandleScope handle_scope (isolate);
198+ Local<Context> context = env ()->context ();
199+ Context::Scope context_scope (context);
200+ Local<String> result;
201+ if (!String::NewFromTwoByte (isolate,
202+ message.characters16 (),
203+ NewStringType::kNormal ,
204+ message.length ())
205+ .ToLocal (&result)) {
206+ fprintf (stderr, " Failed to convert profiling message\n " );
207+ }
208+ WriteCpuProfile (result);
209+ }
210+
211+ void V8CpuProfilerConnection::WriteCpuProfile (Local<String> message) {
212+ const std::string& path = env ()->cpu_profile_path ();
213+ CHECK (!path.empty ());
214+ std::string directory = path.substr (0 , path.find_last_of (kPathSeparator ));
215+ if (directory != path) {
216+ uv_fs_t req;
217+ int ret = fs::MKDirpSync (nullptr , &req, directory, 0777 , nullptr );
218+ uv_fs_req_cleanup (&req);
219+ if (ret < 0 && ret != UV_EEXIST) {
220+ char err_buf[128 ];
221+ uv_err_name_r (ret, err_buf, sizeof (err_buf));
222+ fprintf (stderr,
223+ " %s: Failed to create cpu profile directory %s\n " ,
224+ err_buf,
225+ directory.c_str ());
226+ return ;
227+ }
228+ }
229+ MaybeLocal<String> result = GetResult (message);
230+ if (!result.IsEmpty ()) {
231+ WriteResult (path.c_str (), result.ToLocalChecked ());
232+ }
233+ }
234+
235+ MaybeLocal<String> V8CpuProfilerConnection::GetResult (Local<String> message) {
236+ Local<Context> context = env ()->context ();
237+ Isolate* isolate = env ()->isolate ();
238+ Local<Value> parsed;
239+ if (!v8::JSON::Parse (context, message).ToLocal (&parsed) ||
240+ !parsed->IsObject ()) {
241+ fprintf (stderr, " Failed to parse CPU profile result as JSON object\n " );
242+ return MaybeLocal<String>();
243+ }
244+
245+ Local<Value> result_v;
246+ if (!parsed.As <Object>()
247+ ->Get (context, FIXED_ONE_BYTE_STRING (isolate, " result" ))
248+ .ToLocal (&result_v)) {
249+ fprintf (stderr, " Failed to get result from CPU profile message\n " );
250+ return MaybeLocal<String>();
251+ }
252+
253+ if (!result_v->IsObject ()) {
254+ fprintf (stderr, " 'result' from CPU profile message is not an object\n " );
255+ return MaybeLocal<String>();
256+ }
257+
258+ Local<Value> profile_v;
259+ if (!result_v.As <Object>()
260+ ->Get (context, FIXED_ONE_BYTE_STRING (isolate, " profile" ))
261+ .ToLocal (&profile_v)) {
262+ fprintf (stderr, " 'profile' from CPU profile result is undefined\n " );
263+ return MaybeLocal<String>();
264+ }
265+
266+ Local<String> result_s;
267+ if (!v8::JSON::Stringify (context, profile_v).ToLocal (&result_s)) {
268+ fprintf (stderr, " Failed to stringify CPU profile result\n " );
269+ return MaybeLocal<String>();
270+ }
271+
272+ return result_s;
273+ }
274+
275+ void V8CpuProfilerConnection::Start () {
276+ Debug (env (), DebugCategory::INSPECTOR_PROFILER, " Sending Profiler.start\n " );
277+ Isolate* isolate = env ()->isolate ();
278+ Local<String> enable = FIXED_ONE_BYTE_STRING (
279+ isolate, R"( {"id": 1, "method": "Profiler.enable"})" );
280+ Local<String> start = FIXED_ONE_BYTE_STRING (
281+ isolate, R"( {"id": 2, "method": "Profiler.start"})" );
282+ DispatchMessage (enable);
283+ DispatchMessage (start);
284+ }
285+
286+ void V8CpuProfilerConnection::End () {
287+ CHECK_EQ (ending_, false );
288+ ending_ = true ;
289+ Debug (env (), DebugCategory::INSPECTOR_PROFILER, " Sending Profiler.stop\n " );
290+ Isolate* isolate = env ()->isolate ();
291+ HandleScope scope (isolate);
292+ Local<String> end =
293+ FIXED_ONE_BYTE_STRING (isolate, R"( {"id": 3, "method": "Profiler.stop"})" );
294+ DispatchMessage (end);
295+ }
296+
183297// For now, we only support coverage profiling, but we may add more
184298// in the future.
185299void EndStartedProfilers (Environment* env) {
@@ -190,6 +304,12 @@ void EndStartedProfilers(Environment* env) {
190304 env, DebugCategory::INSPECTOR_PROFILER, " Ending coverage collection\n " );
191305 connection->End ();
192306 }
307+
308+ connection = env->cpu_profiler_connection ();
309+ if (connection != nullptr && !connection->ending ()) {
310+ Debug (env, DebugCategory::INSPECTOR_PROFILER, " Ending cpu profiling\n " );
311+ connection->End ();
312+ }
193313}
194314
195315void StartCoverageCollection (Environment* env) {
@@ -198,6 +318,26 @@ void StartCoverageCollection(Environment* env) {
198318 env->coverage_connection ()->Start ();
199319}
200320
321+ void StartCpuProfiling (Environment* env, const std::string& profile_path) {
322+ std::string path;
323+ if (profile_path.empty ()) {
324+ char cwd[CWD_BUFSIZE];
325+ size_t size = CWD_BUFSIZE;
326+ int err = uv_cwd (cwd, &size);
327+ // TODO(joyeecheung): fallback to exec path / argv[0]
328+ CHECK_EQ (err, 0 );
329+ CHECK_GT (size, 0 );
330+ DiagnosticFilename filename (env, " CPU" , " cpuprofile" );
331+ path = cwd + std::string (kPathSeparator ) + (*filename);
332+ } else {
333+ path = profile_path;
334+ }
335+ env->set_cpu_profile_path (std::move (path));
336+ env->set_cpu_profiler_connection (
337+ std::make_unique<V8CpuProfilerConnection>(env));
338+ env->cpu_profiler_connection ()->Start ();
339+ }
340+
201341static void SetCoverageDirectory (const FunctionCallbackInfo<Value>& args) {
202342 CHECK (args[0 ]->IsString ());
203343 Environment* env = Environment::GetCurrent (args);
0 commit comments