12
12
13
13
#include < cinttypes>
14
14
#include < sstream>
15
+ #include " simdutf.h"
15
16
16
17
namespace node {
17
18
namespace profiler {
@@ -38,11 +39,11 @@ V8ProfilerConnection::V8ProfilerConnection(Environment* env)
38
39
false)),
39
40
env_(env) {}
40
41
41
- uint32_t V8ProfilerConnection::DispatchMessage (const char * method,
42
+ uint64_t V8ProfilerConnection::DispatchMessage (const char * method,
42
43
const char * params,
43
44
bool is_profile_request) {
44
45
std::stringstream ss;
45
- uint32_t id = next_id ();
46
+ uint64_t id = next_id ();
46
47
ss << R"( { "id": )" << id;
47
48
DCHECK (method != nullptr );
48
49
ss << R"( , "method": ")" << method << ' "' ;
@@ -67,8 +68,10 @@ uint32_t V8ProfilerConnection::DispatchMessage(const char* method,
67
68
68
69
static void WriteResult (Environment* env,
69
70
const char * path,
70
- Local<String> result) {
71
- int ret = WriteFileSync (env->isolate (), path, result);
71
+ std::string_view profile) {
72
+ uv_buf_t buf =
73
+ uv_buf_init (const_cast <char *>(profile.data ()), profile.length ());
74
+ int ret = WriteFileSync (path, buf);
72
75
if (ret != 0 ) {
73
76
char err_buf[128 ];
74
77
uv_err_name_r (ret, err_buf, sizeof (err_buf));
@@ -78,77 +81,103 @@ static void WriteResult(Environment* env,
78
81
Debug (env, DebugCategory::INSPECTOR_PROFILER, " Written result to %s\n " , path);
79
82
}
80
83
84
+ bool StringViewToUTF8 (const v8_inspector::StringView& source,
85
+ std::vector<char >* utf8_out,
86
+ size_t * utf8_length) {
87
+ size_t source_len = source.length ();
88
+ if (source.is8Bit ()) {
89
+ const char * latin1 = reinterpret_cast <const char *>(source.characters8 ());
90
+ *utf8_length = simdutf::utf8_length_from_latin1 (latin1, source_len);
91
+ utf8_out->resize (*utf8_length);
92
+ size_t result_len =
93
+ simdutf::convert_latin1_to_utf8 (latin1, source_len, utf8_out->data ());
94
+ return *utf8_length == result_len;
95
+ }
96
+
97
+ const char16_t * utf16 =
98
+ reinterpret_cast <const char16_t *>(source.characters16 ());
99
+ *utf8_length = simdutf::utf8_length_from_utf16 (utf16, source_len);
100
+ utf8_out->resize (*utf8_length);
101
+ size_t result_len =
102
+ simdutf::convert_utf16_to_utf8 (utf16, source_len, utf8_out->data ());
103
+ return *utf8_length == result_len;
104
+ }
105
+
81
106
void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend (
82
107
const v8_inspector::StringView& message) {
83
108
Environment* env = connection_->env ();
84
109
Isolate* isolate = env->isolate ();
85
110
HandleScope handle_scope (isolate);
86
111
Local<Context> context = env->context ();
87
112
Context::Scope context_scope (context);
88
-
89
113
const char * type = connection_->type ();
90
- // Convert StringView to a Local<String>.
91
- Local<String> message_str;
92
- if (!String::NewFromTwoByte (isolate,
93
- message.characters16 (),
94
- NewStringType::kNormal ,
95
- message.length ())
96
- .ToLocal (&message_str)) {
97
- fprintf (
98
- stderr, " Failed to convert %s profile message to V8 string\n " , type);
99
- return ;
100
- }
101
114
102
115
Debug (env,
103
116
DebugCategory::INSPECTOR_PROFILER,
104
- " Receive %s profile message\n " ,
117
+ " Received %s profile message\n " ,
105
118
type);
106
119
107
- Local<Value> parsed;
108
- if (!v8::JSON::Parse (context, message_str).ToLocal (&parsed) ||
109
- !parsed->IsObject ()) {
110
- fprintf (stderr, " Failed to parse %s profile result as JSON object\n " , type);
120
+ std::vector<char > message_utf8;
121
+ size_t message_utf8_length;
122
+ if (!StringViewToUTF8 (message, &message_utf8, &message_utf8_length)) {
123
+ fprintf (
124
+ stderr, " Failed to convert %s profile message to UTF8 string\n " , type);
111
125
return ;
112
126
}
127
+ // Allocate extra padding for JSON parsing.
128
+ message_utf8.resize (message_utf8_length + simdjson::SIMDJSON_PADDING);
113
129
114
- Local<Object> response = parsed.As <Object>();
115
- Local<Value> id_v;
116
- if (!response->Get (context, FIXED_ONE_BYTE_STRING (isolate, " id" ))
117
- .ToLocal (&id_v) ||
118
- !id_v->IsUint32 ()) {
119
- Utf8Value str (isolate, message_str);
130
+ simdjson::ondemand::document parsed;
131
+ simdjson::ondemand::object response;
132
+ if (connection_->json_parser_
133
+ .iterate(
134
+ message_utf8.data (), message_utf8_length, message_utf8.size ())
135
+ .get (parsed) ||
136
+ parsed.get_object ().get (response)) {
120
137
fprintf (
121
- stderr, " Cannot retrieve id from the response message:\n %s\n " , *str);
138
+ stderr, " Failed to parse %s profile result as JSON object:\n " , type);
139
+ fprintf (stderr,
140
+ " %.*s\n " ,
141
+ static_cast <int >(message_utf8_length),
142
+ message_utf8.data ());
143
+ return ;
144
+ }
145
+
146
+ uint64_t id;
147
+ if (response[" id" ].get_uint64 ().get (id)) {
148
+ fprintf (stderr, " Cannot retrieve id from %s profile response:\n " , type);
149
+ fprintf (stderr,
150
+ " %.*s\n " ,
151
+ static_cast <int >(message_utf8_length),
152
+ message_utf8.data ());
122
153
return ;
123
154
}
124
- uint32_t id = id_v.As <v8::Uint32>()->Value ();
125
155
126
156
if (!connection_->HasProfileId (id)) {
127
- Utf8Value str (isolate, message_str);
128
- Debug (env, DebugCategory::INSPECTOR_PROFILER, " %s\n " , *str);
157
+ Debug (env,
158
+ DebugCategory::INSPECTOR_PROFILER,
159
+ " %s\n " ,
160
+ std::string_view (message_utf8.data (), message_utf8_length));
129
161
return ;
130
162
} else {
131
163
Debug (env,
132
164
DebugCategory::INSPECTOR_PROFILER,
133
165
" Writing profile response (id = %" PRIu64 " )\n " ,
134
- static_cast < uint64_t >(id) );
166
+ id );
135
167
}
136
168
169
+ simdjson::ondemand::object result;
137
170
// Get message.result from the response.
138
- Local<Value> result_v;
139
- if (!response->Get (context, FIXED_ONE_BYTE_STRING (isolate, " result" ))
140
- .ToLocal (&result_v)) {
141
- fprintf (stderr, " Failed to get 'result' from %s profile response\n " , type);
142
- return ;
143
- }
144
-
145
- if (!result_v->IsObject ()) {
146
- fprintf (
147
- stderr, " 'result' from %s profile response is not an object\n " , type);
171
+ if (response[" result" ].get_object ().get (result)) {
172
+ fprintf (stderr, " Failed to get 'result' from %s profile response:\n " , type);
173
+ fprintf (stderr,
174
+ " %.*s\n " ,
175
+ static_cast <int >(message_utf8_length),
176
+ message_utf8.data ());
148
177
return ;
149
178
}
150
179
151
- connection_->WriteProfile (result_v. As <Object>() );
180
+ connection_->WriteProfile (result );
152
181
connection_->RemoveProfileId (id);
153
182
}
154
183
@@ -178,20 +207,31 @@ std::string V8CoverageConnection::GetFilename() const {
178
207
env ()->thread_id ());
179
208
}
180
209
181
- void V8ProfilerConnection::WriteProfile (Local<Object> result) {
182
- Local<Context> context = env_-> context ();
183
-
184
- // Generate the profile output from the subclass.
185
- Local<Object> profile;
186
- if (! GetProfile ( result). ToLocal (&profile)) {
187
- return ;
210
+ std::optional<std::string_view> V8ProfilerConnection::GetProfile (
211
+ simdjson::ondemand::object& result) {
212
+ simdjson::ondemand::object profile_object;
213
+ if (result[ " profile" ]. get_object (). get (profile_object)) {
214
+ fprintf (
215
+ stderr, " 'profile' from %s profile result is not an Object \n " , type ());
216
+ return std::nullopt ;
188
217
}
218
+ std::string_view profile_raw;
219
+ if (profile_object.raw_json ().get (profile_raw)) {
220
+ fprintf (stderr,
221
+ " Cannot get raw string of the 'profile' field from %s profile\n " ,
222
+ type ());
223
+ return std::nullopt;
224
+ }
225
+ return profile_raw;
226
+ }
189
227
190
- Local<String> result_s;
191
- if (!v8::JSON::Stringify (context, profile).ToLocal (&result_s)) {
192
- fprintf (stderr, " Failed to stringify %s profile result\n " , type ());
228
+ void V8ProfilerConnection::WriteProfile (simdjson::ondemand::object& result) {
229
+ // Generate the profile output from the subclass.
230
+ auto profile_opt = GetProfile (result);
231
+ if (!profile_opt.has_value ()) {
193
232
return ;
194
233
}
234
+ std::string_view profile = profile_opt.value ();
195
235
196
236
// Create the directory if necessary.
197
237
std::string directory = GetDirectory ();
@@ -204,14 +244,12 @@ void V8ProfilerConnection::WriteProfile(Local<Object> result) {
204
244
DCHECK (!filename.empty ());
205
245
std::string path = directory + kPathSeparator + filename;
206
246
207
- WriteResult (env_, path.c_str (), result_s );
247
+ WriteResult (env_, path.c_str (), profile );
208
248
}
209
249
210
- void V8CoverageConnection::WriteProfile (Local<Object> result) {
250
+ void V8CoverageConnection::WriteProfile (simdjson::ondemand::object& result) {
211
251
Isolate* isolate = env_->isolate ();
212
- Local<Context> context = env_->context ();
213
252
HandleScope handle_scope (isolate);
214
- Context::Scope context_scope (context);
215
253
216
254
// This is only set up during pre-execution (when the environment variables
217
255
// becomes available in the JS land). If it's empty, we don't have coverage
@@ -223,11 +261,15 @@ void V8CoverageConnection::WriteProfile(Local<Object> result) {
223
261
return ;
224
262
}
225
263
264
+ Local<Context> context = env_->context ();
265
+ Context::Scope context_scope (context);
266
+
226
267
// Generate the profile output from the subclass.
227
- Local<Object> profile ;
228
- if (!GetProfile (result). ToLocal (&profile )) {
268
+ auto profile_opt = GetProfile (result) ;
269
+ if (!profile_opt. has_value ( )) {
229
270
return ;
230
271
}
272
+ std::string_view profile = profile_opt.value ();
231
273
232
274
// append source-map cache information to coverage object:
233
275
Local<Value> source_map_cache_v;
@@ -246,17 +288,6 @@ void V8CoverageConnection::WriteProfile(Local<Object> result) {
246
288
PrintCaughtException (isolate, context, try_catch);
247
289
}
248
290
}
249
- // Avoid writing to disk if no source-map data:
250
- if (!source_map_cache_v->IsUndefined ()) {
251
- profile->Set (context, FIXED_ONE_BYTE_STRING (isolate, " source-map-cache" ),
252
- source_map_cache_v).ToChecked ();
253
- }
254
-
255
- Local<String> result_s;
256
- if (!v8::JSON::Stringify (context, profile).ToLocal (&result_s)) {
257
- fprintf (stderr, " Failed to stringify %s profile result\n " , type ());
258
- return ;
259
- }
260
291
261
292
// Create the directory if necessary.
262
293
std::string directory = GetDirectory ();
@@ -269,11 +300,58 @@ void V8CoverageConnection::WriteProfile(Local<Object> result) {
269
300
DCHECK (!filename.empty ());
270
301
std::string path = directory + kPathSeparator + filename;
271
302
272
- WriteResult (env_, path.c_str (), result_s);
303
+ // Only insert source map cache when there's source map data at all.
304
+ if (!source_map_cache_v->IsUndefined ()) {
305
+ // It would be more performant to just find the last } and insert the source
306
+ // map cache in front of it, but source map cache is still experimental
307
+ // anyway so just re-parse it with V8 for now.
308
+ Local<String> profile_str;
309
+ if (!v8::String::NewFromUtf8 (isolate,
310
+ profile.data (),
311
+ v8::NewStringType::kNormal ,
312
+ profile.length ())
313
+ .ToLocal (&profile_str)) {
314
+ fprintf (stderr, " Failed to re-parse %s profile as UTF8\n " , type ());
315
+ return ;
316
+ }
317
+ Local<Value> profile_value;
318
+ if (!v8::JSON::Parse (context, profile_str).ToLocal (&profile_value) ||
319
+ !profile_value->IsObject ()) {
320
+ fprintf (stderr, " Failed to re-parse %s profile from JSON\n " , type ());
321
+ return ;
322
+ }
323
+ if (profile_value.As <Object>()
324
+ ->Set (context,
325
+ FIXED_ONE_BYTE_STRING (isolate, " source-map-cache" ),
326
+ source_map_cache_v)
327
+ .IsNothing ()) {
328
+ fprintf (stderr,
329
+ " Failed to insert source map cache into %s profile\n " ,
330
+ type ());
331
+ return ;
332
+ }
333
+ Local<String> result_s;
334
+ if (!v8::JSON::Stringify (context, profile_value).ToLocal (&result_s)) {
335
+ fprintf (stderr, " Failed to stringify %s profile result\n " , type ());
336
+ return ;
337
+ }
338
+ Utf8Value result_utf8 (isolate, result_s);
339
+ WriteResult (env_, path.c_str (), result_utf8.ToStringView ());
340
+ } else {
341
+ WriteResult (env_, path.c_str (), profile);
342
+ }
273
343
}
274
344
275
- MaybeLocal<Object> V8CoverageConnection::GetProfile (Local<Object> result) {
276
- return result;
345
+ std::optional<std::string_view> V8CoverageConnection::GetProfile (
346
+ simdjson::ondemand::object& result) {
347
+ std::string_view profile_raw;
348
+ if (result.raw_json ().get (profile_raw)) {
349
+ fprintf (stderr,
350
+ " Cannot get raw string of the 'profile' field from %s profile\n " ,
351
+ type ());
352
+ return std::nullopt;
353
+ }
354
+ return profile_raw;
277
355
}
278
356
279
357
std::string V8CoverageConnection::GetDirectory () const {
@@ -313,22 +391,6 @@ std::string V8CpuProfilerConnection::GetFilename() const {
313
391
return env ()->cpu_prof_name ();
314
392
}
315
393
316
- MaybeLocal<Object> V8CpuProfilerConnection::GetProfile (Local<Object> result) {
317
- Local<Value> profile_v;
318
- if (!result
319
- ->Get (env ()->context (),
320
- FIXED_ONE_BYTE_STRING (env ()->isolate (), " profile" ))
321
- .ToLocal (&profile_v)) {
322
- fprintf (stderr, " 'profile' from CPU profile result is undefined\n " );
323
- return MaybeLocal<Object>();
324
- }
325
- if (!profile_v->IsObject ()) {
326
- fprintf (stderr, " 'profile' from CPU profile result is not an Object\n " );
327
- return MaybeLocal<Object>();
328
- }
329
- return profile_v.As <Object>();
330
- }
331
-
332
394
void V8CpuProfilerConnection::Start () {
333
395
DispatchMessage (" Profiler.enable" );
334
396
std::string params = R"( { "interval": )" ;
@@ -357,22 +419,6 @@ std::string V8HeapProfilerConnection::GetFilename() const {
357
419
return env ()->heap_prof_name ();
358
420
}
359
421
360
- MaybeLocal<Object> V8HeapProfilerConnection::GetProfile (Local<Object> result) {
361
- Local<Value> profile_v;
362
- if (!result
363
- ->Get (env ()->context (),
364
- FIXED_ONE_BYTE_STRING (env ()->isolate (), " profile" ))
365
- .ToLocal (&profile_v)) {
366
- fprintf (stderr, " 'profile' from heap profile result is undefined\n " );
367
- return MaybeLocal<Object>();
368
- }
369
- if (!profile_v->IsObject ()) {
370
- fprintf (stderr, " 'profile' from heap profile result is not an Object\n " );
371
- return MaybeLocal<Object>();
372
- }
373
- return profile_v.As <Object>();
374
- }
375
-
376
422
void V8HeapProfilerConnection::Start () {
377
423
DispatchMessage (" HeapProfiler.enable" );
378
424
std::string params = R"( { "samplingInterval": )" ;
0 commit comments