@@ -33,6 +33,7 @@ namespace v8_utils {
33
33
using v8::Array;
34
34
using v8::Context;
35
35
using v8::FunctionCallbackInfo;
36
+ using v8::FunctionTemplate;
36
37
using v8::HandleScope;
37
38
using v8::HeapCodeStatistics;
38
39
using v8::HeapSpaceStatistics;
@@ -210,6 +211,180 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
210
211
V8::SetFlagsFromString (*flags, static_cast <size_t >(flags.length ()));
211
212
}
212
213
214
+ static const char * GetGCTypeName (v8::GCType gc_type) {
215
+ switch (gc_type) {
216
+ case v8::GCType::kGCTypeScavenge :
217
+ return " Scavenge" ;
218
+ case v8::GCType::kGCTypeMarkSweepCompact :
219
+ return " MarkSweepCompact" ;
220
+ case v8::GCType::kGCTypeIncrementalMarking :
221
+ return " IncrementalMarking" ;
222
+ case v8::GCType::kGCTypeProcessWeakCallbacks :
223
+ return " ProcessWeakCallbacks" ;
224
+ default :
225
+ return " UnKnow" ;
226
+ }
227
+ }
228
+
229
+ static void SetHeapStatistics (JSONWriter* writer, Isolate * isolate) {
230
+ HeapStatistics heap_statistics;
231
+ isolate->GetHeapStatistics (&heap_statistics);
232
+ writer->json_objectstart (" heapStatistics" );
233
+ writer->json_keyvalue (" totalHeapSize" , heap_statistics.total_heap_size ());
234
+ writer->json_keyvalue (" totalHeapSizeExecutable" ,
235
+ heap_statistics.total_heap_size_executable ());
236
+ writer->json_keyvalue (" totalPhysicalSize" ,
237
+ heap_statistics.total_physical_size ());
238
+ writer->json_keyvalue (" totalAvailableSize" ,
239
+ heap_statistics.total_available_size ());
240
+ writer->json_keyvalue (" totalGlobalHandlesSize" ,
241
+ heap_statistics.total_global_handles_size ());
242
+ writer->json_keyvalue (" usedGlobalHandlesSize" ,
243
+ heap_statistics.used_global_handles_size ());
244
+ writer->json_keyvalue (" usedHeapSize" ,
245
+ heap_statistics.used_heap_size ());
246
+ writer->json_keyvalue (" heapSizeLimit" ,
247
+ heap_statistics.heap_size_limit ());
248
+ writer->json_keyvalue (" mallocedMemory" ,
249
+ heap_statistics.malloced_memory ());
250
+ writer->json_keyvalue (" externalMemory" ,
251
+ heap_statistics.external_memory ());
252
+ writer->json_keyvalue (" peakMallocedMemory" ,
253
+ heap_statistics.peak_malloced_memory ());
254
+ writer->json_objectend ();
255
+
256
+ int space_count = isolate->NumberOfHeapSpaces ();
257
+ writer->json_arraystart (" heapSpaceStatistics" );
258
+ for (int i = 0 ; i < space_count; i++) {
259
+ HeapSpaceStatistics heap_space_statistics;
260
+ isolate->GetHeapSpaceStatistics (&heap_space_statistics, i);
261
+ writer->json_start ();
262
+ writer->json_keyvalue (" spaceName" ,
263
+ heap_space_statistics.space_name ());
264
+ writer->json_keyvalue (" spaceSize" ,
265
+ heap_space_statistics.space_size ());
266
+ writer->json_keyvalue (" spaceUsedSize" ,
267
+ heap_space_statistics.space_used_size ());
268
+ writer->json_keyvalue (" spaceAvailableSize" ,
269
+ heap_space_statistics.space_available_size ());
270
+ writer->json_keyvalue (" physicalSpaceSize" ,
271
+ heap_space_statistics.physical_space_size ());
272
+ writer->json_end ();
273
+ }
274
+ writer->json_arrayend ();
275
+ }
276
+
277
+ static void BeforeGCCallback (Isolate* isolate,
278
+ v8::GCType gc_type,
279
+ v8::GCCallbackFlags flags,
280
+ void * data) {
281
+ GCProfiler* profiler = static_cast <GCProfiler *>(data);
282
+ if (profiler->current_gc_type () != 0 ) {
283
+ return ;
284
+ }
285
+ profiler->set_current_gc_type (gc_type);
286
+ profiler->set_start_time (uv_hrtime ());
287
+ JSONWriter* writer = profiler->writer ();
288
+ writer->json_start ();
289
+ writer->json_keyvalue (" gcType" , GetGCTypeName (gc_type));
290
+ writer->json_objectstart (" beforeGC" );
291
+ SetHeapStatistics (writer, isolate);
292
+ writer->json_objectend ();
293
+ }
294
+
295
+ static void AfterGCCallback (Isolate* isolate,
296
+ v8::GCType gc_type,
297
+ v8::GCCallbackFlags flags,
298
+ void * data) {
299
+ GCProfiler* profiler = static_cast <GCProfiler *>(data);
300
+ if (profiler->current_gc_type () != gc_type) {
301
+ return ;
302
+ }
303
+ JSONWriter* writer = profiler->writer ();
304
+ profiler->set_current_gc_type (0 );
305
+ u_int64_t start_time = profiler->start_time ();
306
+ profiler->set_start_time (0 );
307
+ writer->json_keyvalue (" cost" , (uv_hrtime () - start_time) / 1e3 );
308
+ writer->json_objectstart (" afterGC" );
309
+ SetHeapStatistics (writer, isolate);
310
+ writer->json_objectend ();
311
+ writer->json_end ();
312
+ }
313
+
314
+ GCProfiler::GCProfiler (Environment* env, Local<Object> object):
315
+ BaseObject(env, object), writer_(outfile_, false ) {
316
+ MakeWeak ();
317
+ }
318
+
319
+ // This function will be called when
320
+ // 1. StartGCProfile and StopGCProfile are called and
321
+ // JS land do not keep the object any more.
322
+ // 2. StartGCProfile is called then the env exits before
323
+ // StopGCProfile is called.
324
+ GCProfiler::~GCProfiler () {
325
+ if (state_ != GCProfilerState::kInitialized ) {
326
+ env ()->isolate ()->RemoveGCPrologueCallback (BeforeGCCallback, this );
327
+ env ()->isolate ()->RemoveGCEpilogueCallback (AfterGCCallback, this );
328
+ }
329
+ }
330
+
331
+ void GCProfiler::New (const FunctionCallbackInfo<Value>& args) {
332
+ CHECK (args.IsConstructCall ());
333
+ Environment* env = Environment::GetCurrent (args);
334
+ new GCProfiler (env, args.This ());
335
+ }
336
+
337
+ void GCProfiler::StartGCProfile (const FunctionCallbackInfo<Value>& args) {
338
+ GCProfiler* profiler;
339
+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
340
+ if (profiler->state () != GCProfilerState::kInitialized ) {
341
+ return ;
342
+ }
343
+ Environment* env = Environment::GetCurrent (args);
344
+ Isolate* isolate = args.GetIsolate ();
345
+ node::Utf8Value filename (env->isolate (), args[0 ]);
346
+ profiler->outfile ()->open (*filename, std::ios::out | std::ios::binary);
347
+ if (!profiler->outfile ()->is_open ()) {
348
+ env->ThrowError (" failed to open file" );
349
+ return ;
350
+ }
351
+ profiler->writer ()->json_start ();
352
+ profiler->writer ()->json_keyvalue (" version" , 1 );
353
+
354
+ uv_timeval64_t ts;
355
+ if (uv_gettimeofday (&ts) == 0 ) {
356
+ profiler->writer ()->json_keyvalue (" startTime" ,
357
+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
358
+ } else {
359
+ profiler->writer ()->json_keyvalue (" startTime" , 0 );
360
+ }
361
+ profiler->writer ()->json_arraystart (" statistics" );
362
+ isolate->AddGCPrologueCallback (BeforeGCCallback,
363
+ static_cast <void *>(profiler));
364
+ isolate->AddGCEpilogueCallback (AfterGCCallback,
365
+ static_cast <void *>(profiler));
366
+ profiler->set_state (GCProfilerState::kStarted );
367
+ }
368
+
369
+ void GCProfiler::StopGCProfile (const FunctionCallbackInfo<v8::Value>& args) {
370
+ GCProfiler* profiler;
371
+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
372
+ if (profiler->state () != GCProfilerState::kStarted ) {
373
+ return ;
374
+ }
375
+ profiler->writer ()->json_arrayend ();
376
+ uv_timeval64_t ts;
377
+ if (uv_gettimeofday (&ts) == 0 ) {
378
+ profiler->writer ()->json_keyvalue (" endtTime" ,
379
+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
380
+ } else {
381
+ profiler->writer ()->json_keyvalue (" endtTime" , 0 );
382
+ }
383
+ profiler->writer ()->json_end ();
384
+ profiler->outfile ()->close ();
385
+ profiler->set_state (GCProfilerState::kStopped );
386
+ }
387
+
213
388
void Initialize (Local<Object> target,
214
389
Local<Value> unused,
215
390
Local<Context> context,
@@ -272,6 +447,17 @@ void Initialize(Local<Object> target,
272
447
273
448
// Export symbols used by v8.setFlagsFromString()
274
449
SetMethod (context, target, " setFlagsFromString" , SetFlagsFromString);
450
+
451
+ // GCProfiler
452
+ Local<FunctionTemplate> t = NewFunctionTemplate (env->isolate (),
453
+ GCProfiler::New);
454
+ t->InstanceTemplate ()->SetInternalFieldCount (
455
+ BaseObject::kInternalFieldCount );
456
+ SetProtoMethod (env->isolate (), t, " startGCProfile" ,
457
+ GCProfiler::StartGCProfile);
458
+ SetProtoMethod (env->isolate (), t, " stopGCProfile" ,
459
+ GCProfiler::StopGCProfile);
460
+ SetConstructorFunction (context, target, " GCProfiler" , t);
275
461
}
276
462
277
463
void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
@@ -281,6 +467,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
281
467
registry->Register (UpdateHeapSpaceStatisticsBuffer);
282
468
registry->Register (SetFlagsFromString);
283
469
registry->Register (SetHeapSnapshotNearHeapLimit);
470
+ registry->Register (GCProfiler::StartGCProfile);
471
+ registry->Register (GCProfiler::StopGCProfile);
284
472
}
285
473
286
474
} // namespace v8_utils
0 commit comments