33#include " gc-heap-snapshot.h"
44
55#include " julia_internal.h"
6+ #include " julia_assert.h"
67#include " gc.h"
78
89#include " llvm/ADT/StringMap.h"
1112#include < vector>
1213#include < string>
1314#include < sstream>
15+ #include < iostream>
16+ #include < set>
1417
1518using std::vector;
1619using std::string;
20+ using std::set;
1721using std::ostringstream;
1822using std::pair;
1923using std::make_pair;
@@ -70,7 +74,7 @@ struct Node {
7074 size_t id; // This should be a globally-unique counter, but we use the memory address
7175 size_t self_size;
7276 size_t trace_node_id; // This is ALWAYS 0 in Javascript heap-snapshots.
73- // whether the from_node is attached or dettached from the main application state
77+ // whether the from_node is attached or detached from the main application state
7478 // https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/include/v8-profiler.h#L739-L745
7579 int detachedness; // 0 - unknown, 1 - attached, 2 - detached
7680 vector<Edge> edges;
@@ -115,6 +119,8 @@ struct HeapSnapshot {
115119 DenseMap<void *, size_t > node_ptr_to_index_map;
116120
117121 size_t num_edges = 0 ; // For metadata, updated as you add each edge. Needed because edges owned by nodes.
122+ size_t _gc_root_idx = 1 ; // node index of the GC roots node
123+ size_t _gc_finlist_root_idx = 2 ; // node index of the GC finlist roots node
118124};
119125
120126// global heap snapshot, mutated by garbage collector
@@ -127,13 +133,13 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
127133static inline void _record_gc_edge (const char *edge_type,
128134 jl_value_t *a, jl_value_t *b, size_t name_or_index) JL_NOTSAFEPOINT;
129135void _record_gc_just_edge (const char *edge_type, Node &from_node, size_t to_idx, size_t name_or_idx) JL_NOTSAFEPOINT;
130- void _add_internal_root (HeapSnapshot *snapshot);
136+ void _add_synthetic_root_entries (HeapSnapshot *snapshot);
131137
132138
133139JL_DLLEXPORT void jl_gc_take_heap_snapshot (ios_t *stream, char all_one)
134140{
135141 HeapSnapshot snapshot;
136- _add_internal_root (&snapshot);
142+ _add_synthetic_root_entries (&snapshot);
137143
138144 jl_mutex_lock (&heapsnapshot_lock);
139145
@@ -155,10 +161,12 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *stream, char all_one)
155161 serialize_heap_snapshot ((ios_t *)stream, snapshot, all_one);
156162}
157163
158- // adds a node at id 0 which is the "uber root":
159- // a synthetic node which points to all the GC roots.
160- void _add_internal_root (HeapSnapshot *snapshot)
164+ // mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L212
165+ // add synthetic nodes for the uber root, the GC roots, and the GC finalizer list roots
166+ void _add_synthetic_root_entries (HeapSnapshot *snapshot)
161167{
168+ // adds a node at id 0 which is the "uber root":
169+ // a synthetic node which points to all the GC roots.
162170 Node internal_root{
163171 snapshot->node_types .find_or_create_string_id (" synthetic" ),
164172 snapshot->names .find_or_create_string_id (" " ), // name
@@ -169,6 +177,44 @@ void _add_internal_root(HeapSnapshot *snapshot)
169177 vector<Edge>() // outgoing edges
170178 };
171179 snapshot->nodes .push_back (internal_root);
180+
181+ // Add a node for the GC roots
182+ snapshot->_gc_root_idx = snapshot->nodes .size ();
183+ Node gc_roots{
184+ snapshot->node_types .find_or_create_string_id (" synthetic" ),
185+ snapshot->names .find_or_create_string_id (" GC roots" ), // name
186+ snapshot->_gc_root_idx , // id
187+ 0 , // size
188+ 0 , // size_t trace_node_id (unused)
189+ 0 , // int detachedness; // 0 - unknown, 1 - attached; 2 - detached
190+ vector<Edge>() // outgoing edges
191+ };
192+ snapshot->nodes .push_back (gc_roots);
193+ snapshot->nodes .front ().edges .push_back (Edge{
194+ snapshot->edge_types .find_or_create_string_id (" internal" ),
195+ snapshot->names .find_or_create_string_id (" GC roots" ), // edge label
196+ snapshot->_gc_root_idx // to
197+ });
198+ snapshot->num_edges += 1 ;
199+
200+ // add a node for the gc finalizer list roots
201+ snapshot->_gc_finlist_root_idx = snapshot->nodes .size ();
202+ Node gc_finlist_roots{
203+ snapshot->node_types .find_or_create_string_id (" synthetic" ),
204+ snapshot->names .find_or_create_string_id (" GC finalizer list roots" ), // name
205+ snapshot->_gc_finlist_root_idx , // id
206+ 0 , // size
207+ 0 , // size_t trace_node_id (unused)
208+ 0 , // int detachedness; // 0 - unknown, 1 - attached; 2 - detached
209+ vector<Edge>() // outgoing edges
210+ };
211+ snapshot->nodes .push_back (gc_finlist_roots);
212+ snapshot->nodes .front ().edges .push_back (Edge{
213+ snapshot->edge_types .find_or_create_string_id (" internal" ),
214+ snapshot->names .find_or_create_string_id (" GC finlist roots" ), // edge label
215+ snapshot->_gc_finlist_root_idx // to
216+ });
217+ snapshot->num_edges += 1 ;
172218}
173219
174220// mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L597-L597
@@ -326,6 +372,26 @@ void _gc_heap_snapshot_record_root(jl_value_t *root, char *name) JL_NOTSAFEPOINT
326372 _record_gc_just_edge (" internal" , internal_root, to_node_idx, edge_label);
327373}
328374
375+ void _gc_heap_snapshot_record_gc_roots (jl_value_t *root, char *name) JL_NOTSAFEPOINT
376+ {
377+ record_node_to_gc_snapshot (root);
378+
379+ auto from_node_idx = g_snapshot->_gc_root_idx ;
380+ auto to_node_idx = record_node_to_gc_snapshot (root);
381+ auto edge_label = g_snapshot->names .find_or_create_string_id (name);
382+ _record_gc_just_edge (" internal" , g_snapshot->nodes [from_node_idx], to_node_idx, edge_label);
383+ }
384+
385+ void _gc_heap_snapshot_record_finlist (jl_value_t *obj, size_t index) JL_NOTSAFEPOINT
386+ {
387+ auto from_node_idx = g_snapshot->_gc_finlist_root_idx ;
388+ auto to_node_idx = record_node_to_gc_snapshot (obj);
389+ ostringstream ss;
390+ ss << " finlist-" << index;
391+ auto edge_label = g_snapshot->names .find_or_create_string_id (ss.str ());
392+ _record_gc_just_edge (" internal" , g_snapshot->nodes [from_node_idx], to_node_idx, edge_label);
393+ }
394+
329395// Add a node to the heap snapshot representing a Julia stack frame.
330396// Each task points at a stack frame, which points at the stack frame of
331397// the function it's currently calling, forming a linked list.
@@ -490,6 +556,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
490556
491557 ios_printf (stream, " \" nodes\" :[" );
492558 bool first_node = true ;
559+ // use a set to track the nodes that do not have parents
560+ set<size_t > orphans;
493561 for (const auto &from_node : snapshot.nodes ) {
494562 if (first_node) {
495563 first_node = false ;
@@ -506,6 +574,14 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
506574 from_node.edges .size (),
507575 from_node.trace_node_id ,
508576 from_node.detachedness );
577+ if (from_node.id != snapshot._gc_root_idx && from_node.id != snapshot._gc_finlist_root_idx ) {
578+ // find the node index from the node object pointer
579+ void * ptr = (void *)from_node.id ;
580+ size_t n_id = snapshot.node_ptr_to_index_map [ptr];
581+ orphans.insert (n_id);
582+ } else {
583+ orphans.insert (from_node.id );
584+ }
509585 }
510586 ios_printf (stream, " ],\n " );
511587
@@ -523,6 +599,12 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
523599 edge.type ,
524600 edge.name_or_index ,
525601 edge.to_node * k_node_number_of_fields);
602+ auto n_id = edge.to_node ;
603+ auto it = orphans.find (n_id);
604+ if (it != orphans.end ()) {
605+ // remove the node from the orphans if it has at least one incoming edge
606+ orphans.erase (it);
607+ }
526608 }
527609 }
528610 ios_printf (stream, " ],\n " ); // end "edges"
@@ -532,4 +614,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
532614 snapshot.names .print_json_array (stream, true );
533615
534616 ios_printf (stream, " }" );
617+
618+ // remove the uber node from the orphans
619+ orphans.erase (0 );
620+ assert (orphans.size () == 0 && " all nodes except the uber node should have at least one incoming edge" );
535621}
0 commit comments