1212
1313#include < cinttypes>
1414#include < sstream>
15+ #include " simdutf.h"
1516
1617namespace node {
1718namespace profiler {
@@ -38,11 +39,11 @@ V8ProfilerConnection::V8ProfilerConnection(Environment* env)
3839 false)),
3940 env_(env) {}
4041
41- uint32_t V8ProfilerConnection::DispatchMessage (const char * method,
42+ uint64_t V8ProfilerConnection::DispatchMessage (const char * method,
4243 const char * params,
4344 bool is_profile_request) {
4445 std::stringstream ss;
45- uint32_t id = next_id ();
46+ uint64_t id = next_id ();
4647 ss << R"( { "id": )" << id;
4748 DCHECK (method != nullptr );
4849 ss << R"( , "method": ")" << method << ' "' ;
@@ -67,8 +68,10 @@ uint32_t V8ProfilerConnection::DispatchMessage(const char* method,
6768
6869static void WriteResult (Environment* env,
6970 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);
7275 if (ret != 0 ) {
7376 char err_buf[128 ];
7477 uv_err_name_r (ret, err_buf, sizeof (err_buf));
@@ -78,77 +81,103 @@ static void WriteResult(Environment* env,
7881 Debug (env, DebugCategory::INSPECTOR_PROFILER, " Written result to %s\n " , path);
7982}
8083
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+
81106void V8ProfilerConnection::V8ProfilerSessionDelegate::SendMessageToFrontend (
82107 const v8_inspector::StringView& message) {
83108 Environment* env = connection_->env ();
84109 Isolate* isolate = env->isolate ();
85110 HandleScope handle_scope (isolate);
86111 Local<Context> context = env->context ();
87112 Context::Scope context_scope (context);
88-
89113 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- }
101114
102115 Debug (env,
103116 DebugCategory::INSPECTOR_PROFILER,
104- " Receive %s profile message\n " ,
117+ " Received %s profile message\n " ,
105118 type);
106119
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);
111125 return ;
112126 }
127+ // Allocate extra padding for JSON parsing.
128+ message_utf8.resize (message_utf8_length + simdjson::SIMDJSON_PADDING);
113129
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)) {
120137 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 ());
122153 return ;
123154 }
124- uint32_t id = id_v.As <v8::Uint32>()->Value ();
125155
126156 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));
129161 return ;
130162 } else {
131163 Debug (env,
132164 DebugCategory::INSPECTOR_PROFILER,
133165 " Writing profile response (id = %" PRIu64 " )\n " ,
134- static_cast < uint64_t >(id) );
166+ id );
135167 }
136168
169+ simdjson::ondemand::object result;
137170 // 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 ());
148177 return ;
149178 }
150179
151- connection_->WriteProfile (result_v. As <Object>() );
180+ connection_->WriteProfile (result );
152181 connection_->RemoveProfileId (id);
153182}
154183
@@ -178,20 +207,31 @@ std::string V8CoverageConnection::GetFilename() const {
178207 env ()->thread_id ());
179208}
180209
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 ;
188217 }
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+ }
189227
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 ()) {
193232 return ;
194233 }
234+ std::string_view profile = profile_opt.value ();
195235
196236 // Create the directory if necessary.
197237 std::string directory = GetDirectory ();
@@ -204,14 +244,12 @@ void V8ProfilerConnection::WriteProfile(Local<Object> result) {
204244 DCHECK (!filename.empty ());
205245 std::string path = directory + kPathSeparator + filename;
206246
207- WriteResult (env_, path.c_str (), result_s );
247+ WriteResult (env_, path.c_str (), profile );
208248}
209249
210- void V8CoverageConnection::WriteProfile (Local<Object> result) {
250+ void V8CoverageConnection::WriteProfile (simdjson::ondemand::object& result) {
211251 Isolate* isolate = env_->isolate ();
212- Local<Context> context = env_->context ();
213252 HandleScope handle_scope (isolate);
214- Context::Scope context_scope (context);
215253
216254 // This is only set up during pre-execution (when the environment variables
217255 // 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) {
223261 return ;
224262 }
225263
264+ Local<Context> context = env_->context ();
265+ Context::Scope context_scope (context);
266+
226267 // 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 ( )) {
229270 return ;
230271 }
272+ std::string_view profile = profile_opt.value ();
231273
232274 // append source-map cache information to coverage object:
233275 Local<Value> source_map_cache_v;
@@ -246,17 +288,6 @@ void V8CoverageConnection::WriteProfile(Local<Object> result) {
246288 PrintCaughtException (isolate, context, try_catch);
247289 }
248290 }
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- }
260291
261292 // Create the directory if necessary.
262293 std::string directory = GetDirectory ();
@@ -269,11 +300,58 @@ void V8CoverageConnection::WriteProfile(Local<Object> result) {
269300 DCHECK (!filename.empty ());
270301 std::string path = directory + kPathSeparator + filename;
271302
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+ }
273343}
274344
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;
277355}
278356
279357std::string V8CoverageConnection::GetDirectory () const {
@@ -313,22 +391,6 @@ std::string V8CpuProfilerConnection::GetFilename() const {
313391 return env ()->cpu_prof_name ();
314392}
315393
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-
332394void V8CpuProfilerConnection::Start () {
333395 DispatchMessage (" Profiler.enable" );
334396 std::string params = R"( { "interval": )" ;
@@ -357,22 +419,6 @@ std::string V8HeapProfilerConnection::GetFilename() const {
357419 return env ()->heap_prof_name ();
358420}
359421
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-
376422void V8HeapProfilerConnection::Start () {
377423 DispatchMessage (" HeapProfiler.enable" );
378424 std::string params = R"( { "samplingInterval": )" ;
0 commit comments