9
9
#include " util-inl.h"
10
10
#include " v8-inspector.h"
11
11
12
+ #include < cinttypes>
12
13
#include < sstream>
13
14
14
15
namespace node {
@@ -36,10 +37,11 @@ V8ProfilerConnection::V8ProfilerConnection(Environment* env)
36
37
false)),
37
38
env_(env) {}
38
39
39
- size_t V8ProfilerConnection::DispatchMessage (const char * method,
40
- const char * params) {
40
+ uint32_t V8ProfilerConnection::DispatchMessage (const char * method,
41
+ const char * params,
42
+ bool is_profile_request) {
41
43
std::stringstream ss;
42
- size_t id = next_id ();
44
+ uint32_t id = next_id ();
43
45
ss << R"( { "id": )" << id;
44
46
DCHECK (method != nullptr );
45
47
ss << R"( , "method": ")" << method << ' "' ;
@@ -50,12 +52,15 @@ size_t V8ProfilerConnection::DispatchMessage(const char* method,
50
52
std::string message = ss.str ();
51
53
const uint8_t * message_data =
52
54
reinterpret_cast <const uint8_t *>(message.c_str ());
55
+ // Save the id of the profile request to identify its response.
56
+ if (is_profile_request) {
57
+ profile_ids_.insert (id);
58
+ }
53
59
Debug (env (),
54
60
DebugCategory::INSPECTOR_PROFILER,
55
61
" Dispatching message %s\n " ,
56
62
message.c_str ());
57
63
session_->Dispatch (StringView (message_data, message.length ()));
58
- // TODO(joyeecheung): use this to identify the ending message.
59
64
return id;
60
65
}
61
66
@@ -77,33 +82,73 @@ void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
77
82
Environment* env = connection_->env ();
78
83
Isolate* isolate = env->isolate ();
79
84
HandleScope handle_scope (isolate);
80
- Context::Scope context_scope (env->context ());
85
+ Local<Context> context = env->context ();
86
+ Context::Scope context_scope (context);
81
87
82
- // TODO(joyeecheung): always parse the message so that we can use the id to
83
- // identify ending messages as well as printing the message in the debug
84
- // output when there is an error.
85
88
const char * type = connection_->type ();
86
- Debug (env,
87
- DebugCategory::INSPECTOR_PROFILER,
88
- " Receive %s profile message, ending = %s\n " ,
89
- type,
90
- connection_->ending () ? " true" : " false" );
91
- if (!connection_->ending ()) {
92
- return ;
93
- }
94
-
95
89
// Convert StringView to a Local<String>.
96
90
Local<String> message_str;
97
91
if (!String::NewFromTwoByte (isolate,
98
92
message.characters16 (),
99
93
NewStringType::kNormal ,
100
94
message.length ())
101
95
.ToLocal (&message_str)) {
102
- fprintf (stderr, " Failed to convert %s profile message\n " , type);
96
+ fprintf (
97
+ stderr, " Failed to convert %s profile message to V8 string\n " , type);
98
+ return ;
99
+ }
100
+
101
+ Debug (env,
102
+ DebugCategory::INSPECTOR_PROFILER,
103
+ " Receive %s profile message\n " ,
104
+ type);
105
+
106
+ Local<Value> parsed;
107
+ if (!v8::JSON::Parse (context, message_str).ToLocal (&parsed) ||
108
+ !parsed->IsObject ()) {
109
+ fprintf (stderr, " Failed to parse %s profile result as JSON object\n " , type);
103
110
return ;
104
111
}
105
112
106
- connection_->WriteProfile (message_str);
113
+ Local<Object> response = parsed.As <Object>();
114
+ Local<Value> id_v;
115
+ if (!response->Get (context, FIXED_ONE_BYTE_STRING (isolate, " id" ))
116
+ .ToLocal (&id_v) ||
117
+ !id_v->IsUint32 ()) {
118
+ Utf8Value str (isolate, message_str);
119
+ fprintf (
120
+ stderr, " Cannot retrieve id from the response message:\n %s\n " , *str);
121
+ return ;
122
+ }
123
+ uint32_t id = id_v.As <v8::Uint32>()->Value ();
124
+
125
+ if (!connection_->HasProfileId (id)) {
126
+ Utf8Value str (isolate, message_str);
127
+ Debug (env, DebugCategory::INSPECTOR_PROFILER, " %s\n " , *str);
128
+ return ;
129
+ } else {
130
+ Debug (env,
131
+ DebugCategory::INSPECTOR_PROFILER,
132
+ " Writing profile response (id = %" PRIu64 " )\n " ,
133
+ static_cast <uint64_t >(id));
134
+ }
135
+
136
+ // Get message.result from the response.
137
+ Local<Value> result_v;
138
+ if (!response->Get (context, FIXED_ONE_BYTE_STRING (isolate, " result" ))
139
+ .ToLocal (&result_v)) {
140
+ fprintf (stderr, " Failed to get 'result' from %s profile response\n " , type);
141
+ return ;
142
+ }
143
+
144
+ if (!result_v->IsObject ()) {
145
+ fprintf (
146
+ stderr, " 'result' from %s profile response is not an object\n " , type);
147
+ return ;
148
+ }
149
+
150
+ connection_->WriteProfile (result_v.As <Object>());
151
+ connection_->RemoveProfileId (id);
107
152
}
108
153
109
154
static bool EnsureDirectory (const std::string& directory, const char * type) {
@@ -138,45 +183,9 @@ std::string V8CoverageConnection::GetFilename() const {
138
183
return filename;
139
184
}
140
185
141
- static MaybeLocal<Object> ParseProfile (Environment* env,
142
- Local<String> message,
143
- const char * type) {
144
- Local<Context> context = env->context ();
145
- Isolate* isolate = env->isolate ();
146
-
147
- // Get message.result from the response
148
- Local<Value> parsed;
149
- if (!v8::JSON::Parse (context, message).ToLocal (&parsed) ||
150
- !parsed->IsObject ()) {
151
- fprintf (stderr, " Failed to parse %s profile result as JSON object\n " , type);
152
- return MaybeLocal<Object>();
153
- }
154
-
155
- Local<Value> result_v;
156
- if (!parsed.As <Object>()
157
- ->Get (context, FIXED_ONE_BYTE_STRING (isolate, " result" ))
158
- .ToLocal (&result_v)) {
159
- fprintf (stderr, " Failed to get 'result' from %s profile message\n " , type);
160
- return MaybeLocal<Object>();
161
- }
162
-
163
- if (!result_v->IsObject ()) {
164
- fprintf (
165
- stderr, " 'result' from %s profile message is not an object\n " , type);
166
- return MaybeLocal<Object>();
167
- }
168
-
169
- return result_v.As <Object>();
170
- }
171
-
172
- void V8ProfilerConnection::WriteProfile (Local<String> message) {
186
+ void V8ProfilerConnection::WriteProfile (Local<Object> result) {
173
187
Local<Context> context = env_->context ();
174
188
175
- // Get message.result from the response.
176
- Local<Object> result;
177
- if (!ParseProfile (env_, message, type ()).ToLocal (&result)) {
178
- return ;
179
- }
180
189
// Generate the profile output from the subclass.
181
190
Local<Object> profile;
182
191
if (!GetProfile (result).ToLocal (&profile)) {
@@ -203,7 +212,7 @@ void V8ProfilerConnection::WriteProfile(Local<String> message) {
203
212
WriteResult (env_, path.c_str (), result_s);
204
213
}
205
214
206
- void V8CoverageConnection::WriteProfile (Local<String> message ) {
215
+ void V8CoverageConnection::WriteProfile (Local<Object> result ) {
207
216
Isolate* isolate = env_->isolate ();
208
217
Local<Context> context = env_->context ();
209
218
HandleScope handle_scope (isolate);
@@ -219,11 +228,6 @@ void V8CoverageConnection::WriteProfile(Local<String> message) {
219
228
return ;
220
229
}
221
230
222
- // Get message.result from the response.
223
- Local<Object> result;
224
- if (!ParseProfile (env_, message, type ()).ToLocal (&result)) {
225
- return ;
226
- }
227
231
// Generate the profile output from the subclass.
228
232
Local<Object> profile;
229
233
if (!GetProfile (result).ToLocal (&profile)) {
@@ -287,10 +291,19 @@ void V8CoverageConnection::Start() {
287
291
R"( { "callCount": true, "detailed": true })" );
288
292
}
289
293
294
+ void V8CoverageConnection::TakeCoverage () {
295
+ DispatchMessage (" Profiler.takePreciseCoverage" , nullptr , true );
296
+ }
297
+
290
298
void V8CoverageConnection::End () {
291
- CHECK_EQ (ending_, false );
299
+ Debug (env_,
300
+ DebugCategory::INSPECTOR_PROFILER,
301
+ " V8CoverageConnection::End(), ending = %d\n " , ending_);
302
+ if (ending_) {
303
+ return ;
304
+ }
292
305
ending_ = true ;
293
- DispatchMessage ( " Profiler.takePreciseCoverage " );
306
+ TakeCoverage ( );
294
307
}
295
308
296
309
std::string V8CpuProfilerConnection::GetDirectory () const {
@@ -327,9 +340,14 @@ void V8CpuProfilerConnection::Start() {
327
340
}
328
341
329
342
void V8CpuProfilerConnection::End () {
330
- CHECK_EQ (ending_, false );
343
+ Debug (env_,
344
+ DebugCategory::INSPECTOR_PROFILER,
345
+ " V8CpuProfilerConnection::End(), ending = %d\n " , ending_);
346
+ if (ending_) {
347
+ return ;
348
+ }
331
349
ending_ = true ;
332
- DispatchMessage (" Profiler.stop" );
350
+ DispatchMessage (" Profiler.stop" , nullptr , true );
333
351
}
334
352
335
353
std::string V8HeapProfilerConnection::GetDirectory () const {
@@ -365,31 +383,33 @@ void V8HeapProfilerConnection::Start() {
365
383
}
366
384
367
385
void V8HeapProfilerConnection::End () {
368
- CHECK_EQ (ending_, false );
386
+ Debug (env_,
387
+ DebugCategory::INSPECTOR_PROFILER,
388
+ " V8HeapProfilerConnection::End(), ending = %d\n " , ending_);
389
+ if (ending_) {
390
+ return ;
391
+ }
369
392
ending_ = true ;
370
- DispatchMessage (" HeapProfiler.stopSampling" );
393
+ DispatchMessage (" HeapProfiler.stopSampling" , nullptr , true );
371
394
}
372
395
373
396
// For now, we only support coverage profiling, but we may add more
374
397
// in the future.
375
398
static void EndStartedProfilers (Environment* env) {
399
+ // TODO(joyeechueng): merge these connections and use one session per env.
376
400
Debug (env, DebugCategory::INSPECTOR_PROFILER, " EndStartedProfilers\n " );
377
401
V8ProfilerConnection* connection = env->cpu_profiler_connection ();
378
- if (connection != nullptr && !connection->ending ()) {
379
- Debug (env, DebugCategory::INSPECTOR_PROFILER, " Ending cpu profiling\n " );
402
+ if (connection != nullptr ) {
380
403
connection->End ();
381
404
}
382
405
383
406
connection = env->heap_profiler_connection ();
384
- if (connection != nullptr && !connection->ending ()) {
385
- Debug (env, DebugCategory::INSPECTOR_PROFILER, " Ending heap profiling\n " );
407
+ if (connection != nullptr ) {
386
408
connection->End ();
387
409
}
388
410
389
411
connection = env->coverage_connection ();
390
- if (connection != nullptr && !connection->ending ()) {
391
- Debug (
392
- env, DebugCategory::INSPECTOR_PROFILER, " Ending coverage collection\n " );
412
+ if (connection != nullptr ) {
393
413
connection->End ();
394
414
}
395
415
}
@@ -453,13 +473,30 @@ static void SetSourceMapCacheGetter(const FunctionCallbackInfo<Value>& args) {
453
473
env->set_source_map_cache_getter (args[0 ].As <Function>());
454
474
}
455
475
476
+ static void TakeCoverage (const FunctionCallbackInfo<Value>& args) {
477
+ Environment* env = Environment::GetCurrent (args);
478
+ V8CoverageConnection* connection = env->coverage_connection ();
479
+
480
+ Debug (
481
+ env,
482
+ DebugCategory::INSPECTOR_PROFILER,
483
+ " TakeCoverage, connection %s nullptr\n " ,
484
+ connection == nullptr ? " ==" : " !=" );
485
+
486
+ if (connection != nullptr ) {
487
+ Debug (env, DebugCategory::INSPECTOR_PROFILER, " taking coverage\n " );
488
+ connection->TakeCoverage ();
489
+ }
490
+ }
491
+
456
492
static void Initialize (Local<Object> target,
457
493
Local<Value> unused,
458
494
Local<Context> context,
459
495
void * priv) {
460
496
Environment* env = Environment::GetCurrent (context);
461
497
env->SetMethod (target, " setCoverageDirectory" , SetCoverageDirectory);
462
498
env->SetMethod (target, " setSourceMapCacheGetter" , SetSourceMapCacheGetter);
499
+ env->SetMethod (target, " takeCoverage" , TakeCoverage);
463
500
}
464
501
465
502
} // namespace profiler
0 commit comments