3
3
#include " gc-heap-snapshot.h"
4
4
5
5
#include " julia_internal.h"
6
+ #include " julia_assert.h"
6
7
#include " gc.h"
7
8
8
9
#include " llvm/ADT/StringMap.h"
11
12
#include < vector>
12
13
#include < string>
13
14
#include < sstream>
15
+ #include < iostream>
16
+ #include < set>
14
17
15
18
using std::vector;
16
19
using std::string;
20
+ using std::set;
17
21
using std::ostringstream;
18
22
using std::pair;
19
23
using std::make_pair;
@@ -115,6 +119,8 @@ struct HeapSnapshot {
115
119
DenseMap<void *, size_t > node_ptr_to_index_map;
116
120
117
121
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
118
124
};
119
125
120
126
// global heap snapshot, mutated by garbage collector
@@ -127,13 +133,13 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
127
133
static inline void _record_gc_edge (const char *edge_type,
128
134
jl_value_t *a, jl_value_t *b, size_t name_or_index) JL_NOTSAFEPOINT;
129
135
void _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);
131
137
132
138
133
139
JL_DLLEXPORT void jl_gc_take_heap_snapshot (ios_t *stream, char all_one)
134
140
{
135
141
HeapSnapshot snapshot;
136
- _add_internal_root (&snapshot);
142
+ _add_synthetic_root_entries (&snapshot);
137
143
138
144
jl_mutex_lock (&heapsnapshot_lock);
139
145
@@ -155,10 +161,12 @@ JL_DLLEXPORT void jl_gc_take_heap_snapshot(ios_t *stream, char all_one)
155
161
serialize_heap_snapshot ((ios_t *)stream, snapshot, all_one);
156
162
}
157
163
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)
161
167
{
168
+ // adds a node at id 0 which is the "uber root":
169
+ // a synthetic node which points to all the GC roots.
162
170
Node internal_root{
163
171
snapshot->node_types .find_or_create_string_id (" synthetic" ),
164
172
snapshot->names .find_or_create_string_id (" " ), // name
@@ -169,6 +177,44 @@ void _add_internal_root(HeapSnapshot *snapshot)
169
177
vector<Edge>() // outgoing edges
170
178
};
171
179
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 ;
172
218
}
173
219
174
220
// 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
326
372
_record_gc_just_edge (" internal" , internal_root, to_node_idx, edge_label);
327
373
}
328
374
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
+
329
395
// Add a node to the heap snapshot representing a Julia stack frame.
330
396
// Each task points at a stack frame, which points at the stack frame of
331
397
// 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
392
458
g_snapshot->names .find_or_create_string_id (path));
393
459
}
394
460
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
396
462
{
397
- jl_globalref_t *globalref = binding->globalref ;
398
- jl_sym_t *name = globalref->name ;
399
463
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);
408
466
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
+ }
416
474
417
475
void _gc_heap_snapshot_record_internal_array_edge (jl_value_t *from, jl_value_t *to) JL_NOTSAFEPOINT
418
476
{
@@ -491,6 +549,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
491
549
492
550
ios_printf (stream, " \" nodes\" :[" );
493
551
bool first_node = true ;
552
+ // use a set to track the nodes that do not have parents
553
+ set<size_t > orphans;
494
554
for (const auto &from_node : snapshot.nodes ) {
495
555
if (first_node) {
496
556
first_node = false ;
@@ -507,6 +567,14 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
507
567
from_node.edges .size (),
508
568
from_node.trace_node_id ,
509
569
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
+ }
510
578
}
511
579
ios_printf (stream, " ],\n " );
512
580
@@ -524,6 +592,12 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
524
592
edge.type ,
525
593
edge.name_or_index ,
526
594
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
+ }
527
601
}
528
602
}
529
603
ios_printf (stream, " ],\n " ); // end "edges"
@@ -533,4 +607,8 @@ void serialize_heap_snapshot(ios_t *stream, HeapSnapshot &snapshot, char all_one
533
607
snapshot.names .print_json_array (stream, true );
534
608
535
609
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" );
536
614
}
0 commit comments