99#include " util-inl.h"
1010#include " v8-inspector.h"
1111
12+ #include < cinttypes>
1213#include < sstream>
1314
1415namespace node {
@@ -36,10 +37,11 @@ V8ProfilerConnection::V8ProfilerConnection(Environment* env)
3637 false)),
3738 env_(env) {}
3839
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) {
4143 std::stringstream ss;
42- size_t id = next_id ();
44+ uint32_t id = next_id ();
4345 ss << R"( { "id": )" << id;
4446 DCHECK (method != nullptr );
4547 ss << R"( , "method": ")" << method << ' "' ;
@@ -50,12 +52,15 @@ size_t V8ProfilerConnection::DispatchMessage(const char* method,
5052 std::string message = ss.str ();
5153 const uint8_t * message_data =
5254 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+ }
5359 Debug (env (),
5460 DebugCategory::INSPECTOR_PROFILER,
5561 " Dispatching message %s\n " ,
5662 message.c_str ());
5763 session_->Dispatch (StringView (message_data, message.length ()));
58- // TODO(joyeecheung): use this to identify the ending message.
5964 return id;
6065}
6166
@@ -77,33 +82,73 @@ void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend(
7782 Environment* env = connection_->env ();
7883 Isolate* isolate = env->isolate ();
7984 HandleScope handle_scope (isolate);
80- Context::Scope context_scope (env->context ());
85+ Local<Context> context = env->context ();
86+ Context::Scope context_scope (context);
8187
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.
8588 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-
9589 // Convert StringView to a Local<String>.
9690 Local<String> message_str;
9791 if (!String::NewFromTwoByte (isolate,
9892 message.characters16 (),
9993 NewStringType::kNormal ,
10094 message.length ())
10195 .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);
103110 return ;
104111 }
105112
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);
107152}
108153
109154static bool EnsureDirectory (const std::string& directory, const char * type) {
@@ -138,45 +183,9 @@ std::string V8CoverageConnection::GetFilename() const {
138183 return filename;
139184}
140185
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) {
173187 Local<Context> context = env_->context ();
174188
175- // Get message.result from the response.
176- Local<Object> result;
177- if (!ParseProfile (env_, message, type ()).ToLocal (&result)) {
178- return ;
179- }
180189 // Generate the profile output from the subclass.
181190 Local<Object> profile;
182191 if (!GetProfile (result).ToLocal (&profile)) {
@@ -203,7 +212,7 @@ void V8ProfilerConnection::WriteProfile(Local<String> message) {
203212 WriteResult (env_, path.c_str (), result_s);
204213}
205214
206- void V8CoverageConnection::WriteProfile (Local<String> message ) {
215+ void V8CoverageConnection::WriteProfile (Local<Object> result ) {
207216 Isolate* isolate = env_->isolate ();
208217 Local<Context> context = env_->context ();
209218 HandleScope handle_scope (isolate);
@@ -219,11 +228,6 @@ void V8CoverageConnection::WriteProfile(Local<String> message) {
219228 return ;
220229 }
221230
222- // Get message.result from the response.
223- Local<Object> result;
224- if (!ParseProfile (env_, message, type ()).ToLocal (&result)) {
225- return ;
226- }
227231 // Generate the profile output from the subclass.
228232 Local<Object> profile;
229233 if (!GetProfile (result).ToLocal (&profile)) {
@@ -287,10 +291,19 @@ void V8CoverageConnection::Start() {
287291 R"( { "callCount": true, "detailed": true })" );
288292}
289293
294+ void V8CoverageConnection::TakeCoverage () {
295+ DispatchMessage (" Profiler.takePreciseCoverage" , nullptr , true );
296+ }
297+
290298void 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+ }
292305 ending_ = true ;
293- DispatchMessage ( " Profiler.takePreciseCoverage " );
306+ TakeCoverage ( );
294307}
295308
296309std::string V8CpuProfilerConnection::GetDirectory () const {
@@ -327,9 +340,14 @@ void V8CpuProfilerConnection::Start() {
327340}
328341
329342void 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+ }
331349 ending_ = true ;
332- DispatchMessage (" Profiler.stop" );
350+ DispatchMessage (" Profiler.stop" , nullptr , true );
333351}
334352
335353std::string V8HeapProfilerConnection::GetDirectory () const {
@@ -365,31 +383,33 @@ void V8HeapProfilerConnection::Start() {
365383}
366384
367385void 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+ }
369392 ending_ = true ;
370- DispatchMessage (" HeapProfiler.stopSampling" );
393+ DispatchMessage (" HeapProfiler.stopSampling" , nullptr , true );
371394}
372395
373396// For now, we only support coverage profiling, but we may add more
374397// in the future.
375398static void EndStartedProfilers (Environment* env) {
399+ // TODO(joyeechueng): merge these connections and use one session per env.
376400 Debug (env, DebugCategory::INSPECTOR_PROFILER, " EndStartedProfilers\n " );
377401 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 ) {
380403 connection->End ();
381404 }
382405
383406 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 ) {
386408 connection->End ();
387409 }
388410
389411 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 ) {
393413 connection->End ();
394414 }
395415}
@@ -453,13 +473,30 @@ static void SetSourceMapCacheGetter(const FunctionCallbackInfo<Value>& args) {
453473 env->set_source_map_cache_getter (args[0 ].As <Function>());
454474}
455475
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+
456492static void Initialize (Local<Object> target,
457493 Local<Value> unused,
458494 Local<Context> context,
459495 void * priv) {
460496 Environment* env = Environment::GetCurrent (context);
461497 env->SetMethod (target, " setCoverageDirectory" , SetCoverageDirectory);
462498 env->SetMethod (target, " setSourceMapCacheGetter" , SetSourceMapCacheGetter);
499+ env->SetMethod (target, " takeCoverage" , TakeCoverage);
463500}
464501
465502} // namespace profiler
0 commit comments