@@ -33,6 +33,7 @@ namespace v8_utils {
3333using v8::Array;
3434using v8::Context;
3535using v8::FunctionCallbackInfo;
36+ using v8::FunctionTemplate;
3637using v8::HandleScope;
3738using v8::HeapCodeStatistics;
3839using v8::HeapSpaceStatistics;
@@ -210,6 +211,184 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
210211 V8::SetFlagsFromString (*flags, static_cast <size_t >(flags.length ()));
211212}
212213
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 " Unknown" ;
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->current_gc_type = gc_type;
286+ profiler->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->current_gc_type = 0 ;
299+ writer->json_keyvalue (" cost" , (uv_hrtime () - profiler->start_time ) / 1e3 );
300+ profiler->start_time = 0 ;
301+ writer->json_objectstart (" afterGC" );
302+ SetHeapStatistics (writer, isolate);
303+ writer->json_objectend ();
304+ writer->json_end ();
305+ }
306+
307+ GCProfiler::GCProfiler (Environment* env, Local<Object> object)
308+ : BaseObject(env, object),
309+ start_time(0 ),
310+ current_gc_type(0 ),
311+ state(GCProfilerState::kInitialized ),
312+ writer_(out_stream_, false ) {
313+ MakeWeak ();
314+ }
315+
316+ // This function will be called when
317+ // 1. StartGCProfile and StopGCProfile are called and
318+ // JS land does not keep the object anymore.
319+ // 2. StartGCProfile is called then the env exits before
320+ // StopGCProfile is called.
321+ GCProfiler::~GCProfiler () {
322+ if (state != GCProfiler::GCProfilerState::kInitialized ) {
323+ env ()->isolate ()->RemoveGCPrologueCallback (BeforeGCCallback, this );
324+ env ()->isolate ()->RemoveGCEpilogueCallback (AfterGCCallback, this );
325+ }
326+ }
327+
328+ JSONWriter* GCProfiler::writer () {
329+ return &writer_;
330+ }
331+
332+ std::ostringstream* GCProfiler::out_stream () {
333+ return &out_stream_;
334+ }
335+
336+ void GCProfiler::New (const FunctionCallbackInfo<Value>& args) {
337+ CHECK (args.IsConstructCall ());
338+ Environment* env = Environment::GetCurrent (args);
339+ new GCProfiler (env, args.This ());
340+ }
341+
342+ void GCProfiler::Start (const FunctionCallbackInfo<Value>& args) {
343+ Environment* env = Environment::GetCurrent (args);
344+ GCProfiler* profiler;
345+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
346+ if (profiler->state != GCProfiler::GCProfilerState::kInitialized ) {
347+ return ;
348+ }
349+ profiler->writer ()->json_start ();
350+ profiler->writer ()->json_keyvalue (" version" , 1 );
351+
352+ uv_timeval64_t ts;
353+ if (uv_gettimeofday (&ts) == 0 ) {
354+ profiler->writer ()->json_keyvalue (" startTime" ,
355+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
356+ } else {
357+ profiler->writer ()->json_keyvalue (" startTime" , 0 );
358+ }
359+ profiler->writer ()->json_arraystart (" statistics" );
360+ env->isolate ()->AddGCPrologueCallback (BeforeGCCallback,
361+ static_cast <void *>(profiler));
362+ env->isolate ()->AddGCEpilogueCallback (AfterGCCallback,
363+ static_cast <void *>(profiler));
364+ profiler->state = GCProfiler::GCProfilerState::kStarted ;
365+ }
366+
367+ void GCProfiler::Stop (const FunctionCallbackInfo<v8::Value>& args) {
368+ Environment* env = Environment::GetCurrent (args);
369+ GCProfiler* profiler;
370+ ASSIGN_OR_RETURN_UNWRAP (&profiler, args.Holder ());
371+ if (profiler->state != GCProfiler::GCProfilerState::kStarted ) {
372+ return ;
373+ }
374+ profiler->writer ()->json_arrayend ();
375+ uv_timeval64_t ts;
376+ if (uv_gettimeofday (&ts) == 0 ) {
377+ profiler->writer ()->json_keyvalue (" endTime" ,
378+ ts.tv_sec * 1000 + ts.tv_usec / 1000 );
379+ } else {
380+ profiler->writer ()->json_keyvalue (" endTime" , 0 );
381+ }
382+ profiler->writer ()->json_end ();
383+ profiler->state = GCProfiler::GCProfilerState::kStopped ;
384+ auto string = profiler->out_stream ()->str ();
385+ args.GetReturnValue ().Set (String::NewFromUtf8 (env->isolate (),
386+ string.data (),
387+ v8::NewStringType::kNormal ,
388+ string.size ())
389+ .ToLocalChecked ());
390+ }
391+
213392void Initialize (Local<Object> target,
214393 Local<Value> unused,
215394 Local<Context> context,
@@ -272,6 +451,14 @@ void Initialize(Local<Object> target,
272451
273452 // Export symbols used by v8.setFlagsFromString()
274453 SetMethod (context, target, " setFlagsFromString" , SetFlagsFromString);
454+
455+ // GCProfiler
456+ Local<FunctionTemplate> t =
457+ NewFunctionTemplate (env->isolate (), GCProfiler::New);
458+ t->InstanceTemplate ()->SetInternalFieldCount (BaseObject::kInternalFieldCount );
459+ SetProtoMethod (env->isolate (), t, " start" , GCProfiler::Start);
460+ SetProtoMethod (env->isolate (), t, " stop" , GCProfiler::Stop);
461+ SetConstructorFunction (context, target, " GCProfiler" , t);
275462}
276463
277464void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
@@ -281,6 +468,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
281468 registry->Register (UpdateHeapSpaceStatisticsBuffer);
282469 registry->Register (SetFlagsFromString);
283470 registry->Register (SetHeapSnapshotNearHeapLimit);
471+ registry->Register (GCProfiler::New);
472+ registry->Register (GCProfiler::Start);
473+ registry->Register (GCProfiler::Stop);
284474}
285475
286476} // namespace v8_utils
0 commit comments