@@ -23,10 +23,14 @@ using v8::Value;
23
23
using v8_inspector::StringBuffer;
24
24
using v8_inspector::StringView;
25
25
26
- #ifdef __POSIX__
27
- const char * const kPathSeparator = " /" ;
28
- #else
26
+ #ifdef _WIN32
29
27
const 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)
30
34
#endif
31
35
32
36
std::unique_ptr<StringBuffer> ToProtocolString (Isolate* isolate,
@@ -180,6 +184,116 @@ void V8CoverageConnection::End() {
180
184
DispatchMessage (end);
181
185
}
182
186
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
+
183
297
// For now, we only support coverage profiling, but we may add more
184
298
// in the future.
185
299
void EndStartedProfilers (Environment* env) {
@@ -190,6 +304,12 @@ void EndStartedProfilers(Environment* env) {
190
304
env, DebugCategory::INSPECTOR_PROFILER, " Ending coverage collection\n " );
191
305
connection->End ();
192
306
}
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
+ }
193
313
}
194
314
195
315
void StartCoverageCollection (Environment* env) {
@@ -198,6 +318,26 @@ void StartCoverageCollection(Environment* env) {
198
318
env->coverage_connection ()->Start ();
199
319
}
200
320
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
+
201
341
static void SetCoverageDirectory (const FunctionCallbackInfo<Value>& args) {
202
342
CHECK (args[0 ]->IsString ());
203
343
Environment* env = Environment::GetCurrent (args);
0 commit comments