@@ -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,117 @@ 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+ Local<Context> context = env ()->context ();
198+ HandleScope handle_scope (isolate);
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 covert profiling message\n " );
207+ }
208+ WriteCpuProfile (result);
209+ }
210+
211+ bool 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 false ;
227+ }
228+ }
229+ MaybeLocal<String> result = GetResult (message);
230+ if (result.IsEmpty ()) {
231+ return false ;
232+ }
233+ return WriteResult (path.c_str (), result.ToLocalChecked ());
234+ }
235+
236+ MaybeLocal<String> V8CpuProfilerConnection::GetResult (Local<String> message) {
237+ Local<Context> context = env ()->context ();
238+ Isolate* isolate = env ()->isolate ();
239+ Local<Value> parsed;
240+ if (!v8::JSON::Parse (context, message).ToLocal (&parsed) ||
241+ !parsed->IsObject ()) {
242+ fprintf (stderr, " Failed to parse CPU profile result as JSON object\n " );
243+ return MaybeLocal<String>();
244+ }
245+
246+ Local<Value> result_v;
247+ if (!parsed.As <Object>()
248+ ->Get (context, FIXED_ONE_BYTE_STRING (isolate, " result" ))
249+ .ToLocal (&result_v)) {
250+ fprintf (stderr, " Failed to get result from CPU profile message\n " );
251+ return MaybeLocal<String>();
252+ }
253+
254+ if (!result_v->IsObject ()) {
255+ fprintf (stderr, " 'result' from CPU profile message is not an object\n " );
256+ return MaybeLocal<String>();
257+ }
258+
259+ Local<Value> profile_v;
260+ if (!result_v.As <Object>()
261+ ->Get (context, FIXED_ONE_BYTE_STRING (isolate, " profile" ))
262+ .ToLocal (&profile_v)) {
263+ fprintf (stderr, " 'profile' from CPU profile result is undefined\n " );
264+ return MaybeLocal<String>();
265+ }
266+
267+ Local<String> result_s;
268+ if (!v8::JSON::Stringify (context, profile_v).ToLocal (&result_s)) {
269+ fprintf (stderr, " Failed to stringify CPU profile result\n " );
270+ return MaybeLocal<String>();
271+ }
272+
273+ return result_s;
274+ }
275+
276+ void V8CpuProfilerConnection::Start () {
277+ Debug (env (), DebugCategory::INSPECTOR_PROFILER, " Sending Profiler.start\n " );
278+ Isolate* isolate = env ()->isolate ();
279+ Local<String> enable = FIXED_ONE_BYTE_STRING (
280+ isolate, R"( {"id": 1, "method": "Profiler.enable"})" );
281+ Local<String> start = FIXED_ONE_BYTE_STRING (
282+ isolate, R"( {"id": 2, "method": "Profiler.start"})" );
283+ DispatchMessage (enable);
284+ DispatchMessage (start);
285+ }
286+
287+ void V8CpuProfilerConnection::End () {
288+ CHECK_EQ (ending_, false );
289+ ending_ = true ;
290+ Debug (env (), DebugCategory::INSPECTOR_PROFILER, " Sending Profiler.stop\n " );
291+ Isolate* isolate = env ()->isolate ();
292+ HandleScope scope (isolate);
293+ Local<String> end =
294+ FIXED_ONE_BYTE_STRING (isolate, R"( {"id": 3, "method": "Profiler.stop"})" );
295+ DispatchMessage (end);
296+ }
297+
183298// For now, we only support coverage profiling, but we may add more
184299// in the future.
185300void EndStartedProfilers (Environment* env) {
@@ -190,6 +305,12 @@ void EndStartedProfilers(Environment* env) {
190305 env, DebugCategory::INSPECTOR_PROFILER, " Ending coverage collection\n " );
191306 connection->End ();
192307 }
308+
309+ connection = env->cpu_profiler_connection ();
310+ if (connection != nullptr && !connection->ending ()) {
311+ Debug (env, DebugCategory::INSPECTOR_PROFILER, " Ending cpu profiling\n " );
312+ connection->End ();
313+ }
193314}
194315
195316void StartCoverageCollection (Environment* env) {
@@ -198,6 +319,26 @@ void StartCoverageCollection(Environment* env) {
198319 env->coverage_connection ()->Start ();
199320}
200321
322+ void StartCpuProfiling (Environment* env, const std::string& profile_path) {
323+ std::string path;
324+ if (profile_path.empty ()) {
325+ char cwd[CWD_BUFSIZE];
326+ size_t size = CWD_BUFSIZE;
327+ int err = uv_cwd (cwd, &size);
328+ // TODO(joyeecheung): fallback to exec path / argv[0]
329+ CHECK_EQ (err, 0 );
330+ CHECK_GT (size, 0 );
331+ DiagnosticFilename filename (env, " CPU" , " cpuprofile" );
332+ path = cwd + std::string (kPathSeparator ) + (*filename);
333+ } else {
334+ path = profile_path;
335+ }
336+ env->set_cpu_profile_path (std::move (path));
337+ env->set_cpu_profiler_connection (
338+ std::make_unique<V8CpuProfilerConnection>(env));
339+ env->cpu_profiler_connection ()->Start ();
340+ }
341+
201342static void SetCoverageDirectory (const FunctionCallbackInfo<Value>& args) {
202343 CHECK (args[0 ]->IsString ());
203344 Environment* env = Environment::GetCurrent (args);
0 commit comments