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;
@@ -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+ SmallVector<Edge, 0 >() // 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+ SmallVector<Edge, 0 >() // 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.
@@ -392,27 +458,19 @@ void _gc_heap_snapshot_record_object_edge(jl_value_t *from, jl_value_t *to, void
392458 g_snapshot->names .find_or_create_string_id (path));
393459}
394460
395- void _gc_heap_snapshot_record_module_to_binding (jl_module_t *module , jl_binding_t *binding ) JL_NOTSAFEPOINT
461+ void _gc_heap_snapshot_record_module_to_binding (jl_module_t *module , jl_value_t *bindings, jl_value_t *bindingkeyset ) JL_NOTSAFEPOINT
396462{
397- jl_globalref_t *globalref = binding->globalref ;
398- jl_sym_t *name = globalref->name ;
399463 auto from_node_idx = record_node_to_gc_snapshot ((jl_value_t *)module );
400- auto to_node_idx = record_pointer_to_gc_snapshot (binding, sizeof (jl_binding_t ), jl_symbol_name (name));
401-
402- jl_value_t *value = jl_atomic_load_relaxed (&binding->value );
403- auto value_idx = value ? record_node_to_gc_snapshot (value) : 0 ;
404- jl_value_t *ty = jl_atomic_load_relaxed (&binding->ty );
405- auto ty_idx = ty ? record_node_to_gc_snapshot (ty) : 0 ;
406- auto globalref_idx = record_node_to_gc_snapshot ((jl_value_t *)globalref);
407-
464+ auto to_bindings_idx = record_node_to_gc_snapshot (bindings);
465+ auto to_bindingkeyset_idx = record_node_to_gc_snapshot (bindingkeyset);
408466 auto &from_node = g_snapshot->nodes [from_node_idx];
409- auto &to_node = g_snapshot-> nodes [to_node_idx];
410-
411- _record_gc_just_edge ( " property " , from_node, to_node_idx, g_snapshot-> names . find_or_create_string_id ( " <native> " ));
412- if (value_idx) _record_gc_just_edge ( " internal " , to_node, value_idx, g_snapshot-> names . find_or_create_string_id ( " value " ));
413- if (ty_idx) _record_gc_just_edge (" internal" , to_node, ty_idx , g_snapshot->names .find_or_create_string_id (" ty " ));
414- if (globalref_idx) _record_gc_just_edge ( " internal " , to_node, globalref_idx, g_snapshot-> names . find_or_create_string_id ( " globalref " ));
415- }
467+ if (to_bindings_idx > 0 ) {
468+ _record_gc_just_edge ( " internal " , from_node, to_bindings_idx, g_snapshot-> names . find_or_create_string_id ( " bindings " ));
469+ }
470+ if (to_bindingkeyset_idx > 0 ) {
471+ _record_gc_just_edge (" internal" , from_node, to_bindingkeyset_idx , g_snapshot->names .find_or_create_string_id (" bindingkeyset " ));
472+ }
473+ }
416474
417475void _gc_heap_snapshot_record_internal_array_edge (jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT
418476{
@@ -491,6 +549,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
491549
492550 ios_printf (stream, " \" nodes\" :[" );
493551 bool first_node = true ;
552+ // use a set to track the nodes that do not have parents
553+ set<size_t > orphans;
494554 for (const auto &from_node : snapshot.nodes ) {
495555 if (first_node) {
496556 first_node = false ;
@@ -507,6 +567,14 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
507567 from_node.edges .size (),
508568 from_node.trace_node_id ,
509569 from_node.detachedness );
570+ if (from_node.id != snapshot._gc_root_idx && from_node.id != snapshot._gc_finlist_root_idx ) {
571+ // find the node index from the node object pointer
572+ void * ptr = (void *)from_node.id ;
573+ size_t n_id = snapshot.node_ptr_to_index_map [ptr];
574+ orphans.insert (n_id);
575+ } else {
576+ orphans.insert (from_node.id );
577+ }
510578 }
511579 ios_printf (stream, " ],\n " );
512580
@@ -524,6 +592,12 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
524592 edge.type ,
525593 edge.name_or_index ,
526594 edge.to_node * k_node_number_of_fields);
595+ auto n_id = edge.to_node ;
596+ auto it = orphans.find (n_id);
597+ if (it != orphans.end ()) {
598+ // remove the node from the orphans if it has at least one incoming edge
599+ orphans.erase (it);
600+ }
527601 }
528602 }
529603 ios_printf (stream, " ],\n " ); // end "edges"
@@ -533,4 +607,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
533607 snapshot.names .print_json_array (stream, true );
534608
535609 ios_printf (stream, " }" );
610+
611+ // remove the uber node from the orphans
612+ orphans.erase (0 );
613+ assert (orphans.size () == 0 && " all nodes except the uber node should have at least one incoming edge" );
536614}
0 commit comments