@@ -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,191 @@ 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 () { return state_; }
326
+
327
+ void GCProfiler::set_state (GCProfiler::GCProfilerState state) {
328
+ state_ = state;
329
+ }
330
+
331
+ u_int64_t GCProfiler::start_time () { return start_time_; }
332
+
333
+ void GCProfiler::set_start_time (u_int64_t time) { start_time_ = time; }
334
+
335
+ u_int8_t GCProfiler::current_gc_type () { return current_gc_type_; }
336
+
337
+ void GCProfiler::set_current_gc_type (u_int8_t type) { current_gc_type_ = type; }
338
+
339
+ JSONWriter* GCProfiler::writer () { return &writer_; }
340
+
341
+ std::ofstream* GCProfiler::outfile () { return &outfile_; }
342
+
343
+ void GCProfiler::New (const FunctionCallbackInfo<Value>& args) {
344
+ CHECK (args.IsConstructCall ());
345
+ Environment* env = Environment::GetCurrent (args);
346
+ new GCProfiler (env, args.This ());
347
+ }
348
+
349
+ void GCProfiler::StartGCProfile (const FunctionCallbackInfo<Value>& args) {
350
+ GCProfiler* profiler;
351
+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
352
+ if (profiler->state () != GCProfiler::GCProfilerState::kInitialized ) {
353
+ return ;
354
+ }
355
+ Environment* env = Environment::GetCurrent (args);
356
+ Isolate* isolate = args.GetIsolate ();
357
+ node::Utf8Value filename (env->isolate (), args[0 ]);
358
+ profiler->outfile ()->open (*filename, std::ios::out | std::ios::binary);
359
+ if (!profiler->outfile ()->is_open ()) {
360
+ env->ThrowError (" failed to open file" );
361
+ return ;
362
+ }
363
+ profiler->writer ()->json_start ();
364
+ profiler->writer ()->json_keyvalue (" version" , 1 );
365
+
366
+ uv_timeval64_t ts;
367
+ if (uv_gettimeofday (&ts) == 0 ) {
368
+ profiler->writer ()->json_keyvalue (" startTime" ,
369
+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
370
+ } else {
371
+ profiler->writer ()->json_keyvalue (" startTime" , 0 );
372
+ }
373
+ profiler->writer ()->json_arraystart (" statistics" );
374
+ isolate->AddGCPrologueCallback (BeforeGCCallback,
375
+ static_cast <void *>(profiler));
376
+ isolate->AddGCEpilogueCallback (AfterGCCallback, static_cast <void *>(profiler));
377
+ profiler->set_state (GCProfiler::GCProfilerState::kStarted );
378
+ }
379
+
380
+ void GCProfiler::StopGCProfile (const FunctionCallbackInfo<v8::Value>& args) {
381
+ GCProfiler* profiler;
382
+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
383
+ if (profiler->state () != GCProfiler::GCProfilerState::kStarted ) {
384
+ return ;
385
+ }
386
+ profiler->writer ()->json_arrayend ();
387
+ uv_timeval64_t ts;
388
+ if (uv_gettimeofday (&ts) == 0 ) {
389
+ profiler->writer ()->json_keyvalue (" endtTime" ,
390
+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
391
+ } else {
392
+ profiler->writer ()->json_keyvalue (" endtTime" , 0 );
393
+ }
394
+ profiler->writer ()->json_end ();
395
+ profiler->outfile ()->close ();
396
+ profiler->set_state (GCProfiler::GCProfilerState::kStopped );
397
+ }
398
+
213
399
void Initialize (Local<Object> target,
214
400
Local<Value> unused,
215
401
Local<Context> context,
@@ -272,6 +458,15 @@ void Initialize(Local<Object> target,
272
458
273
459
// Export symbols used by v8.setFlagsFromString()
274
460
SetMethod (context, target, " setFlagsFromString" , SetFlagsFromString);
461
+
462
+ // GCProfiler
463
+ Local<FunctionTemplate> t =
464
+ NewFunctionTemplate (env->isolate (), GCProfiler::New);
465
+ t->InstanceTemplate ()->SetInternalFieldCount (BaseObject::kInternalFieldCount );
466
+ SetProtoMethod (
467
+ env->isolate (), t, " startGCProfile" , GCProfiler::StartGCProfile);
468
+ SetProtoMethod (env->isolate (), t, " stopGCProfile" , GCProfiler::StopGCProfile);
469
+ SetConstructorFunction (context, target, " GCProfiler" , t);
275
470
}
276
471
277
472
void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
@@ -281,6 +476,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
281
476
registry->Register (UpdateHeapSpaceStatisticsBuffer);
282
477
registry->Register (SetFlagsFromString);
283
478
registry->Register (SetHeapSnapshotNearHeapLimit);
479
+ registry->Register (GCProfiler::StartGCProfile);
480
+ registry->Register (GCProfiler::StopGCProfile);
284
481
}
285
482
286
483
} // namespace v8_utils
0 commit comments