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/SmallVector.h"
1213#include < vector>
1314#include < string>
1415#include < sstream>
16+ #include < iostream>
17+ #include < set>
1518
1619using std::string;
20+ using std::set;
1721using std::ostringstream;
1822using std::pair;
1923using std::make_pair;
@@ -116,6 +120,8 @@ struct HeapSnapshot {
116120 DenseMap<void *, size_t > node_ptr_to_index_map;
117121
118122 size_t num_edges = 0 ; // For metadata, updated as you add each edge. Needed because edges owned by nodes.
123+ size_t _gc_root_idx = 1 ; // node index of the GC roots node
124+ size_t _gc_finlist_root_idx = 2 ; // node index of the GC finlist roots node
119125};
120126
121127// global heap snapshot, mutated by garbage collector
@@ -128,13 +134,13 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
128134static inline void _record_gc_edge (const char *edge_type,
129135 jl_value_t *a, jl_value_t *b, size_t name_or_index) JL_NOTSAFEPOINT;
130136void _record_gc_just_edge (const char *edge_type, Node &from_node, size_t to_idx, size_t name_or_idx) JL_NOTSAFEPOINT;
131- void _add_internal_root (HeapSnapshot *snapshot);
137+ void _add_synthetic_root_entries (HeapSnapshot *snapshot);
132138
133139
134140JL_DLLEXPORT void jl_gc_take_heap_snapshot (ios_t *stream, char all_one)
135141{
136142 HeapSnapshot snapshot;
137- _add_internal_root (&snapshot);
143+ _add_synthetic_root_entries (&snapshot);
138144
139145 jl_mutex_lock (&heapsnapshot_lock);
140146
@@ -156,10 +162,12 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *stream, char all_one)
156162 serialize_heap_snapshot ((ios_t *)stream, snapshot, all_one);
157163}
158164
159- // adds a node at id 0 which is the "uber root":
160- // a synthetic node which points to all the GC roots.
161- void _add_internal_root (HeapSnapshot *snapshot)
165+ // mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L212
166+ // add synthetic nodes for the uber root, the GC roots, and the GC finalizer list roots
167+ void _add_synthetic_root_entries (HeapSnapshot *snapshot)
162168{
169+ // adds a node at id 0 which is the "uber root":
170+ // a synthetic node which points to all the GC roots.
163171 Node internal_root{
164172 snapshot->node_types .find_or_create_string_id (" synthetic" ),
165173 snapshot->names .find_or_create_string_id (" " ), // name
@@ -170,6 +178,44 @@ void _add_internal_root(HeapSnapshot *snapshot)
170178 SmallVector<Edge, 0 >() // outgoing edges
171179 };
172180 snapshot->nodes .push_back (internal_root);
181+
182+ // Add a node for the GC roots
183+ snapshot->_gc_root_idx = snapshot->nodes .size ();
184+ Node gc_roots{
185+ snapshot->node_types .find_or_create_string_id (" synthetic" ),
186+ snapshot->names .find_or_create_string_id (" GC roots" ), // name
187+ snapshot->_gc_root_idx , // id
188+ 0 , // size
189+ 0 , // size_t trace_node_id (unused)
190+ 0 , // int detachedness; // 0 - unknown, 1 - attached; 2 - detached
191+ SmallVector<Edge, 0 >() // outgoing edges
192+ };
193+ snapshot->nodes .push_back (gc_roots);
194+ snapshot->nodes .front ().edges .push_back (Edge{
195+ snapshot->edge_types .find_or_create_string_id (" internal" ),
196+ snapshot->names .find_or_create_string_id (" GC roots" ), // edge label
197+ snapshot->_gc_root_idx // to
198+ });
199+ snapshot->num_edges += 1 ;
200+
201+ // add a node for the gc finalizer list roots
202+ snapshot->_gc_finlist_root_idx = snapshot->nodes .size ();
203+ Node gc_finlist_roots{
204+ snapshot->node_types .find_or_create_string_id (" synthetic" ),
205+ snapshot->names .find_or_create_string_id (" GC finalizer list roots" ), // name
206+ snapshot->_gc_finlist_root_idx , // id
207+ 0 , // size
208+ 0 , // size_t trace_node_id (unused)
209+ 0 , // int detachedness; // 0 - unknown, 1 - attached; 2 - detached
210+ SmallVector<Edge, 0 >() // outgoing edges
211+ };
212+ snapshot->nodes .push_back (gc_finlist_roots);
213+ snapshot->nodes .front ().edges .push_back (Edge{
214+ snapshot->edge_types .find_or_create_string_id (" internal" ),
215+ snapshot->names .find_or_create_string_id (" GC finlist roots" ), // edge label
216+ snapshot->_gc_finlist_root_idx // to
217+ });
218+ snapshot->num_edges += 1 ;
173219}
174220
175221// mimicking https://github.com/nodejs/node/blob/5fd7a72e1c4fbaf37d3723c4c81dce35c149dc84/deps/v8/src/profiler/heap-snapshot-generator.cc#L597-L597
@@ -327,6 +373,26 @@ void _gc_heap_snapshot_record_root(jl_value_t *root, char *name) JL_NOTSAFEPOINT
327373 _record_gc_just_edge (" internal" , internal_root, to_node_idx, edge_label);
328374}
329375
376+ void _gc_heap_snapshot_record_gc_roots (jl_value_t *root, char *name) JL_NOTSAFEPOINT
377+ {
378+ record_node_to_gc_snapshot (root);
379+
380+ auto from_node_idx = g_snapshot->_gc_root_idx ;
381+ auto to_node_idx = record_node_to_gc_snapshot (root);
382+ auto edge_label = g_snapshot->names .find_or_create_string_id (name);
383+ _record_gc_just_edge (" internal" , g_snapshot->nodes [from_node_idx], to_node_idx, edge_label);
384+ }
385+
386+ void _gc_heap_snapshot_record_finlist (jl_value_t *obj, size_t index) JL_NOTSAFEPOINT
387+ {
388+ auto from_node_idx = g_snapshot->_gc_finlist_root_idx ;
389+ auto to_node_idx = record_node_to_gc_snapshot (obj);
390+ ostringstream ss;
391+ ss << " finlist-" << index;
392+ auto edge_label = g_snapshot->names .find_or_create_string_id (ss.str ());
393+ _record_gc_just_edge (" internal" , g_snapshot->nodes [from_node_idx], to_node_idx, edge_label);
394+ }
395+
330396// Add a node to the heap snapshot representing a Julia stack frame.
331397// Each task points at a stack frame, which points at the stack frame of
332398// the function it's currently calling, forming a linked list.
@@ -393,27 +459,19 @@ void _gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_value_t *to, void
393459 g_snapshot->names .find_or_create_string_id (path));
394460}
395461
396- void _gc_heap_snapshot_record_module_to_binding (jl_module_t *module , jl_binding_t *binding ) JL_NOTSAFEPOINT
462+ void _gc_heap_snapshot_record_module_to_binding (jl_module_t *module , jl_value_t *bindings, jl_value_t *bindingkeyset ) JL_NOTSAFEPOINT
397463{
398- jl_globalref_t *globalref = binding->globalref ;
399- jl_sym_t *name = globalref->name ;
400464 auto from_node_idx = record_node_to_gc_snapshot ((jl_value_t *)module );
401- auto to_node_idx = record_pointer_to_gc_snapshot (binding, sizeof (jl_binding_t ), jl_symbol_name (name));
402-
403- jl_value_t *value = jl_atomic_load_relaxed (&binding->value );
404- auto value_idx = value ? record_node_to_gc_snapshot (value) : 0 ;
405- jl_value_t *ty = jl_atomic_load_relaxed (&binding->ty );
406- auto ty_idx = ty ? record_node_to_gc_snapshot (ty) : 0 ;
407- auto globalref_idx = record_node_to_gc_snapshot ((jl_value_t *)globalref);
408-
465+ auto to_bindings_idx = record_node_to_gc_snapshot (bindings);
466+ auto to_bindingkeyset_idx = record_node_to_gc_snapshot (bindingkeyset);
409467 auto &from_node = g_snapshot->nodes [from_node_idx];
410- auto &to_node = g_snapshot-> nodes [to_node_idx];
411-
412- _record_gc_just_edge ( " property " , from_node, to_node_idx, g_snapshot-> names . find_or_create_string_id ( " <native> " ));
413- if (value_idx) _record_gc_just_edge ( " internal " , to_node, value_idx, g_snapshot-> names . find_or_create_string_id ( " value " ));
414- if (ty_idx) _record_gc_just_edge (" internal" , to_node, ty_idx , g_snapshot->names .find_or_create_string_id (" ty " ));
415- if (globalref_idx) _record_gc_just_edge ( " internal " , to_node, globalref_idx, g_snapshot-> names . find_or_create_string_id ( " globalref " ));
416- }
468+ if (to_bindings_idx > 0 ) {
469+ _record_gc_just_edge ( " internal " , from_node, to_bindings_idx, g_snapshot-> names . find_or_create_string_id ( " bindings " ));
470+ }
471+ if (to_bindingkeyset_idx > 0 ) {
472+ _record_gc_just_edge (" internal" , from_node, to_bindingkeyset_idx , g_snapshot->names .find_or_create_string_id (" bindingkeyset " ));
473+ }
474+ }
417475
418476void _gc_heap_snapshot_record_internal_array_edge (jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT
419477{
@@ -492,6 +550,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
492550
493551 ios_printf (stream, " \" nodes\" :[" );
494552 bool first_node = true ;
553+ // use a set to track the nodes that do not have parents
554+ set<size_t > orphans;
495555 for (const auto &from_node : snapshot.nodes ) {
496556 if (first_node) {
497557 first_node = false ;
@@ -508,6 +568,14 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
508568 from_node.edges .size (),
509569 from_node.trace_node_id ,
510570 from_node.detachedness );
571+ if (from_node.id != snapshot._gc_root_idx && from_node.id != snapshot._gc_finlist_root_idx ) {
572+ // find the node index from the node object pointer
573+ void * ptr = (void *)from_node.id ;
574+ size_t n_id = snapshot.node_ptr_to_index_map [ptr];
575+ orphans.insert (n_id);
576+ } else {
577+ orphans.insert (from_node.id );
578+ }
511579 }
512580 ios_printf (stream, " ],\n " );
513581
@@ -525,6 +593,12 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
525593 edge.type ,
526594 edge.name_or_index ,
527595 edge.to_node * k_node_number_of_fields);
596+ auto n_id = edge.to_node ;
597+ auto it = orphans.find (n_id);
598+ if (it != orphans.end ()) {
599+ // remove the node from the orphans if it has at least one incoming edge
600+ orphans.erase (it);
601+ }
528602 }
529603 }
530604 ios_printf (stream, " ],\n " ); // end "edges"
@@ -534,4 +608,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
534608 snapshot.names .print_json_array (stream, true );
535609
536610 ios_printf (stream, " }" );
611+
612+ // remove the uber node from the orphans
613+ orphans.erase (0 );
614+ assert (orphans.size () == 0 && " all nodes except the uber node should have at least one incoming edge" );
537615}
0 commit comments