@@ -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,205 @@ 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" , heap_statistics.used_heap_size ());
245
+ writer->json_keyvalue (" heapSizeLimit" , heap_statistics.heap_size_limit ());
246
+ writer->json_keyvalue (" mallocedMemory" , heap_statistics.malloced_memory ());
247
+ writer->json_keyvalue (" externalMemory" , heap_statistics.external_memory ());
248
+ writer->json_keyvalue (" peakMallocedMemory" ,
249
+ heap_statistics.peak_malloced_memory ());
250
+ writer->json_objectend ();
251
+
252
+ int space_count = isolate->NumberOfHeapSpaces ();
253
+ writer->json_arraystart (" heapSpaceStatistics" );
254
+ for (int i = 0 ; i < space_count; i++) {
255
+ HeapSpaceStatistics heap_space_statistics;
256
+ isolate->GetHeapSpaceStatistics (&heap_space_statistics, i);
257
+ writer->json_start ();
258
+ writer->json_keyvalue (" spaceName" , heap_space_statistics.space_name ());
259
+ writer->json_keyvalue (" spaceSize" , heap_space_statistics.space_size ());
260
+ writer->json_keyvalue (" spaceUsedSize" ,
261
+ heap_space_statistics.space_used_size ());
262
+ writer->json_keyvalue (" spaceAvailableSize" ,
263
+ heap_space_statistics.space_available_size ());
264
+ writer->json_keyvalue (" physicalSpaceSize" ,
265
+ heap_space_statistics.physical_space_size ());
266
+ writer->json_end ();
267
+ }
268
+ writer->json_arrayend ();
269
+ }
270
+
271
+ static void BeforeGCCallback (Isolate* isolate,
272
+ v8::GCType gc_type,
273
+ v8::GCCallbackFlags flags,
274
+ void * data) {
275
+ GCProfiler* profiler = static_cast <GCProfiler*>(data);
276
+ if (profiler->current_gc_type () != 0 ) {
277
+ return ;
278
+ }
279
+ JSONWriter* writer = profiler->writer ();
280
+ writer->json_start ();
281
+ writer->json_keyvalue (" gcType" , GetGCTypeName (gc_type));
282
+ writer->json_objectstart (" beforeGC" );
283
+ SetHeapStatistics (writer, isolate);
284
+ writer->json_objectend ();
285
+ profiler->set_current_gc_type (gc_type);
286
+ profiler->set_start_time (uv_hrtime ());
287
+ }
288
+
289
+ static void AfterGCCallback (Isolate* isolate,
290
+ v8::GCType gc_type,
291
+ v8::GCCallbackFlags flags,
292
+ void * data) {
293
+ GCProfiler* profiler = static_cast <GCProfiler*>(data);
294
+ if (profiler->current_gc_type () != gc_type) {
295
+ return ;
296
+ }
297
+ JSONWriter* writer = profiler->writer ();
298
+ profiler->set_current_gc_type (0 );
299
+ u_int64_t start_time = profiler->start_time ();
300
+ profiler->set_start_time (0 );
301
+ writer->json_keyvalue (" cost" , (uv_hrtime () - start_time) / 1e3 );
302
+ writer->json_objectstart (" afterGC" );
303
+ SetHeapStatistics (writer, isolate);
304
+ writer->json_objectend ();
305
+ writer->json_end ();
306
+ }
307
+
308
+ GCProfiler::GCProfiler (Environment* env, Local<Object> object)
309
+ : BaseObject(env, object), writer_(outfile_, false ) {
310
+ MakeWeak ();
311
+ }
312
+
313
+ // This function will be called when
314
+ // 1. StartGCProfile and StopGCProfile are called and
315
+ // JS land do not keep the object any more.
316
+ // 2. StartGCProfile is called then the env exits before
317
+ // StopGCProfile is called.
318
+ GCProfiler::~GCProfiler () {
319
+ if (state_ != GCProfiler::GCProfilerState::kInitialized ) {
320
+ env ()->isolate ()->RemoveGCPrologueCallback (BeforeGCCallback, this );
321
+ env ()->isolate ()->RemoveGCEpilogueCallback (AfterGCCallback, this );
322
+ }
323
+ }
324
+
325
+ GCProfiler::GCProfilerState GCProfiler::state () {
326
+ return state_;
327
+ }
328
+
329
+ void GCProfiler::set_state (GCProfiler::GCProfilerState state) {
330
+ state_ = state;
331
+ }
332
+
333
+ u_int64_t GCProfiler::start_time () {
334
+ return start_time_;
335
+ }
336
+
337
+ void GCProfiler::set_start_time (u_int64_t time) {
338
+ start_time_ = time ;
339
+ }
340
+
341
+ u_int8_t GCProfiler::current_gc_type () {
342
+ return current_gc_type_;
343
+ }
344
+
345
+ void GCProfiler::set_current_gc_type (u_int8_t type) {
346
+ current_gc_type_ = type;
347
+ }
348
+
349
+ JSONWriter* GCProfiler::writer () {
350
+ return &writer_;
351
+ }
352
+
353
+ std::ofstream* GCProfiler::outfile () {
354
+ return &outfile_;
355
+ }
356
+
357
+ void GCProfiler::New (const FunctionCallbackInfo<Value>& args) {
358
+ CHECK (args.IsConstructCall ());
359
+ Environment* env = Environment::GetCurrent (args);
360
+ new GCProfiler (env, args.This ());
361
+ }
362
+
363
+ void GCProfiler::StartGCProfile (const FunctionCallbackInfo<Value>& args) {
364
+ GCProfiler* profiler;
365
+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
366
+ if (profiler->state () != GCProfiler::GCProfilerState::kInitialized ) {
367
+ return ;
368
+ }
369
+ Environment* env = Environment::GetCurrent (args);
370
+ Isolate* isolate = args.GetIsolate ();
371
+ node::Utf8Value filename (env->isolate (), args[0 ]);
372
+ profiler->outfile ()->open (*filename, std::ios::out | std::ios::binary);
373
+ if (!profiler->outfile ()->is_open ()) {
374
+ env->ThrowError (" failed to open file" );
375
+ return ;
376
+ }
377
+ profiler->writer ()->json_start ();
378
+ profiler->writer ()->json_keyvalue (" version" , 1 );
379
+
380
+ uv_timeval64_t ts;
381
+ if (uv_gettimeofday (&ts) == 0 ) {
382
+ profiler->writer ()->json_keyvalue (" startTime" ,
383
+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
384
+ } else {
385
+ profiler->writer ()->json_keyvalue (" startTime" , 0 );
386
+ }
387
+ profiler->writer ()->json_arraystart (" statistics" );
388
+ isolate->AddGCPrologueCallback (BeforeGCCallback,
389
+ static_cast <void *>(profiler));
390
+ isolate->AddGCEpilogueCallback (AfterGCCallback, static_cast <void *>(profiler));
391
+ profiler->set_state (GCProfiler::GCProfilerState::kStarted );
392
+ }
393
+
394
+ void GCProfiler::StopGCProfile (const FunctionCallbackInfo<v8::Value>& args) {
395
+ GCProfiler* profiler;
396
+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
397
+ if (profiler->state () != GCProfiler::GCProfilerState::kStarted ) {
398
+ return ;
399
+ }
400
+ profiler->writer ()->json_arrayend ();
401
+ uv_timeval64_t ts;
402
+ if (uv_gettimeofday (&ts) == 0 ) {
403
+ profiler->writer ()->json_keyvalue (" endtTime" ,
404
+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
405
+ } else {
406
+ profiler->writer ()->json_keyvalue (" endtTime" , 0 );
407
+ }
408
+ profiler->writer ()->json_end ();
409
+ profiler->outfile ()->close ();
410
+ profiler->set_state (GCProfiler::GCProfilerState::kStopped );
411
+ }
412
+
213
413
void Initialize (Local<Object> target,
214
414
Local<Value> unused,
215
415
Local<Context> context,
@@ -272,6 +472,15 @@ void Initialize(Local<Object> target,
272
472
273
473
// Export symbols used by v8.setFlagsFromString()
274
474
SetMethod (context, target, " setFlagsFromString" , SetFlagsFromString);
475
+
476
+ // GCProfiler
477
+ Local<FunctionTemplate> t =
478
+ NewFunctionTemplate (env->isolate (), GCProfiler::New);
479
+ t->InstanceTemplate ()->SetInternalFieldCount (BaseObject::kInternalFieldCount );
480
+ SetProtoMethod (
481
+ env->isolate (), t, " startGCProfile" , GCProfiler::StartGCProfile);
482
+ SetProtoMethod (env->isolate (), t, " stopGCProfile" , GCProfiler::StopGCProfile);
483
+ SetConstructorFunction (context, target, " GCProfiler" , t);
275
484
}
276
485
277
486
void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
@@ -281,6 +490,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
281
490
registry->Register (UpdateHeapSpaceStatisticsBuffer);
282
491
registry->Register (SetFlagsFromString);
283
492
registry->Register (SetHeapSnapshotNearHeapLimit);
493
+ registry->Register (GCProfiler::StartGCProfile);
494
+ registry->Register (GCProfiler::StopGCProfile);
284
495
}
285
496
286
497
} // namespace v8_utils
0 commit comments