22
22
#include <string.h>
24
24
#include "binfile.h"
26
25
#include "stackstash.h"
27
26
#include "profile.h"
30
typedef struct Node Node;
36
while (g_main_pending())
37
g_main_iteration (FALSE);
42
direct_hash_no_null (gconstpointer v)
45
return GPOINTER_TO_UINT (v);
50
ProfileObject *object;
52
Node *siblings; /* siblings in the call tree */
53
Node *children; /* children in the call tree */
54
Node *parent; /* parent in call tree */
55
Node *next; /* nodes that correspond to same object are linked though
70
/* This table is really a cache. We can build it from the call_tree */
71
GHashTable * nodes_by_object;
75
35
create_format (void)
77
SType object_type = 0;
38
SForward *object_forward;
39
SForward *node_forward;
41
format = sformat_new();
43
object_forward = sformat_declare_forward (format);
44
node_forward = sformat_declare_forward (format);
83
sformat_new_integer ("size"),
84
sformat_new_pointer ("call_tree", &node_type),
88
"object", &object_type,
89
sformat_new_string ("name"),
90
sformat_new_integer ("total"),
91
sformat_new_integer ("self"),
49
format, "profile", NULL,
50
sformat_make_integer (format, "size"),
51
sformat_make_pointer (format, "call_tree", node_forward),
53
format, "objects", NULL,
55
format, "object", object_forward,
56
sformat_make_string (format, "name"),
57
sformat_make_integer (format, "total"),
58
sformat_make_integer (format, "self"),
97
sformat_new_pointer ("object", &object_type),
98
sformat_new_pointer ("siblings", &node_type),
99
sformat_new_pointer ("children", &node_type),
100
sformat_new_pointer ("parent", &node_type),
101
sformat_new_integer ("total"),
102
sformat_new_integer ("self"),
103
sformat_new_integer ("toplevel"),
61
format, "nodes", NULL,
63
format, "node", node_forward,
64
sformat_make_pointer (format, "object", object_forward),
65
sformat_make_pointer (format, "siblings", node_forward),
66
sformat_make_pointer (format, "children", node_forward),
67
sformat_make_pointer (format, "parent", node_forward),
68
sformat_make_integer (format, "total"),
69
sformat_make_integer (format, "self"),
70
sformat_make_integer (format, "toplevel"),
109
add_object (gpointer key, gpointer value, gpointer data)
111
SFileOutput *output = data;
112
ProfileObject *object = key;
114
sfile_begin_add_record (output, "object");
116
sfile_add_string (output, "name", object->name);
117
sfile_add_integer (output, "total", object->total);
118
sfile_add_integer (output, "self", object->self);
120
sfile_end_add (output, "object", object);
124
serialize_call_tree (Node *node, SFileOutput *output)
78
serialize_call_tree (StackNode *node,
129
84
sfile_begin_add_record (output, "node");
130
sfile_add_pointer (output, "object", node->object);
85
sfile_add_pointer (output, "object", U64_TO_POINTER (node->data));
131
86
sfile_add_pointer (output, "siblings", node->siblings);
132
87
sfile_add_pointer (output, "children", node->children);
133
88
sfile_add_pointer (output, "parent", node->parent);
134
89
sfile_add_integer (output, "total", node->total);
135
sfile_add_integer (output, "self", node->self);
90
sfile_add_integer (output, "self", node->size);
136
91
sfile_add_integer (output, "toplevel", node->toplevel);
137
92
sfile_end_add (output, "node", node);
139
94
serialize_call_tree (node->siblings, output);
140
95
serialize_call_tree (node->children, output);
105
GList *profile_objects;
150
108
SFormat *format = create_format ();
151
109
SFileOutput *output = sfile_output_new (format);
153
111
sfile_begin_add_record (output, "profile");
155
sfile_add_integer (output, "size", profile->size);
156
sfile_add_pointer (output, "call_tree", profile->call_tree);
113
sfile_add_integer (output, "size", profile_get_size (profile));
114
sfile_add_pointer (output, "call_tree",
115
stack_stash_get_root (profile->stash));
117
profile_objects = profile_get_objects (profile);
158
118
sfile_begin_add_list (output, "objects");
159
g_hash_table_foreach (profile->nodes_by_object, add_object, output);
119
for (list = profile_objects; list != NULL; list = list->next)
121
ProfileObject *object = list->data;
123
sfile_begin_add_record (output, "object");
125
sfile_add_string (output, "name", object->name);
126
sfile_add_integer (output, "total", object->total);
127
sfile_add_integer (output, "self", object->self);
129
sfile_end_add (output, "object", object->name);
131
g_list_foreach (profile_objects, (GFunc)g_free, NULL);
132
g_list_free (profile_objects);
160
134
sfile_end_add (output, "objects", NULL);
162
136
sfile_begin_add_list (output, "nodes");
163
serialize_call_tree (profile->call_tree, output);
137
serialize_call_tree (stack_stash_get_root (profile->stash), output);
164
138
sfile_end_add (output, "nodes", NULL);
166
140
sfile_end_add (output, "profile", NULL);
168
142
result = sfile_output_save (output, file_name, err);
170
sformat_free (format);
171
144
sfile_output_free (output);
145
sformat_free (format);
177
make_hash_table (Node *node, GHashTable *table)
182
g_assert (node->object);
184
node->next = g_hash_table_lookup (table, node->object);
185
g_hash_table_insert (table, node->object, node);
187
g_assert (node->siblings != (void *)0x11);
189
make_hash_table (node->siblings, table);
190
make_hash_table (node->children, table);
194
151
profile_load (const char *filename, GError **err)
197
154
SFileInput *input;
198
155
Profile *profile;
201
159
format = create_format ();
202
160
input = sfile_load (filename, format, err);
207
165
profile = g_new (Profile, 1);
209
profile->nodes_by_object =
210
g_hash_table_new (direct_hash_no_null, g_direct_equal);
212
167
sfile_begin_get_record (input, "profile");
214
sfile_get_integer (input, "size", &profile->size);
215
sfile_get_pointer (input, "call_tree", (void **)&profile->call_tree);
169
sfile_get_integer (input, "size", NULL);
170
sfile_get_pointer (input, "call_tree", (gpointer *)&root);
217
172
n = sfile_begin_get_list (input, "objects");
218
173
for (i = 0; i < n; ++i)
220
ProfileObject *obj = g_new (ProfileObject, 1);
222
177
sfile_begin_get_record (input, "object");
224
sfile_get_string (input, "name", &obj->name);
225
sfile_get_integer (input, "total", (gint32 *)&obj->total);
226
sfile_get_integer (input, "self", (gint32 *)&obj->self);
228
sfile_end_get (input, "object", obj);
179
sfile_get_string (input, "name", &string);
180
sfile_get_integer (input, "total", NULL);
181
sfile_get_integer (input, "self", NULL);
183
sfile_end_get (input, "object", string);
230
185
sfile_end_get (input, "objects", NULL);
232
profile->call_tree = NULL;
187
profile->stash = stack_stash_new ((GDestroyNotify)g_free);
233
189
n = sfile_begin_get_list (input, "nodes");
234
190
for (i = 0; i < n; ++i)
236
Node *node = g_new (Node, 1);
192
StackNode *node = stack_node_new (profile->stash);
238
196
sfile_begin_get_record (input, "node");
240
sfile_get_pointer (input, "object", (gpointer *)&node->object);
198
sfile_get_pointer (input, "object", (gpointer *)&node->data);
241
199
sfile_get_pointer (input, "siblings", (gpointer *)&node->siblings);
242
200
sfile_get_pointer (input, "children", (gpointer *)&node->children);
243
201
sfile_get_pointer (input, "parent", (gpointer *)&node->parent);
244
sfile_get_integer (input, "total", (gint32 *)&node->total);
245
sfile_get_integer (input, "self", (gint32 *)&node->self);
246
sfile_get_integer (input, "toplevel", &node->toplevel);
202
sfile_get_integer (input, "total", &total);
203
sfile_get_integer (input, "self", (gint32 *)&size);
204
sfile_get_integer (input, "toplevel", NULL);
248
209
sfile_end_get (input, "node", node);
250
211
g_assert (node->siblings != (void *)0x11);
252
213
sfile_end_get (input, "nodes", NULL);
253
214
sfile_end_get (input, "profile", NULL);
255
sformat_free (format);
256
216
sfile_input_free (input);
217
sformat_free (format);
219
stack_stash_set_root (profile->stash, root);
258
make_hash_table (profile->call_tree, profile->nodes_by_object);
263
static ProfileObject *
264
profile_object_new (void)
266
ProfileObject *obj = g_new (ProfileObject, 1);
274
profile_object_free (ProfileObject *obj)
281
generate_key (Process *process, gulong address)
285
const Symbol *symbol = process_lookup_symbol (process, address);
287
return g_strdup_printf ("%p%s", (void *)symbol->address, symbol->name);
291
return g_strdup_printf ("p:%p", process_get_cmdline (process));
296
generate_presentation_name (Process *process, gulong address)
299
* using 0 to indicate "process" is broken
303
const Symbol *symbol = process_lookup_symbol (process, address);
305
return g_strdup_printf ("%s", symbol->name);
309
return g_strdup_printf ("%s", process_get_cmdline (process));
314
ensure_profile_object (GHashTable *profile_objects, Process *process, gulong address)
316
char *key = generate_key (process, address);
318
if (!g_hash_table_lookup (profile_objects, key))
320
ProfileObject *object;
322
object = profile_object_new ();
323
object->name = generate_presentation_name (process, address);
325
g_hash_table_insert (profile_objects, key, object);
333
static ProfileObject *
334
lookup_profile_object (GHashTable *profile_objects, Process *process, gulong address)
336
ProfileObject *object;
337
char *key = generate_key (process, address);
338
object = g_hash_table_lookup (profile_objects, key);
344
typedef struct Info Info;
348
GHashTable *profile_objects;
352
generate_object_table (Process *process, GSList *trace, gint size, gpointer data)
357
ensure_profile_object (info->profile_objects, process, 0);
359
for (list = trace; list != NULL; list = list->next)
362
ensure_profile_object (info->profile_objects, process, (gulong)list->data);
365
info->profile->size += size;
371
Node *node = g_new (Node, 1);
372
node->siblings = NULL;
373
node->children = NULL;
384
node_add_trace (Profile *profile, GHashTable *profile_objects, Node *node, Process *process,
385
GSList *trace, gint size,
386
GHashTable *seen_objects)
388
ProfileObject *object;
394
object = lookup_profile_object (profile_objects, process, (gulong)trace->data);
395
for (match = node; match != NULL; match = match->siblings)
397
if (match->object == object)
404
match->object = object;
405
match->siblings = node;
408
if (g_hash_table_lookup (seen_objects, object))
409
match->toplevel = FALSE;
411
match->toplevel = TRUE;
413
match->next = g_hash_table_lookup (profile->nodes_by_object, object);
414
g_hash_table_insert (profile->nodes_by_object, object, match);
417
g_hash_table_insert (seen_objects, object, GINT_TO_POINTER (1));
420
g_print ("%s adds %d\n", match->object->name, size);
422
match->total += size;
426
match->children = node_add_trace (profile, profile_objects, match->children, process, trace->next, size,
434
dump_trace (GSList *trace)
439
g_print ("%x ", trace->data);
447
generate_call_tree (Process *process, GSList *trace, gint size, gpointer data)
451
ProfileObject *proc = lookup_profile_object (info->profile_objects, process, 0);
452
GHashTable *seen_objects;
454
for (match = info->profile->call_tree; match; match = match->siblings)
456
if (match->object == proc)
463
match->object = proc;
464
match->siblings = info->profile->call_tree;
465
info->profile->call_tree = match;
466
match->toplevel = TRUE;
469
g_hash_table_insert (info->profile->nodes_by_object, proc, match);
471
match->total += size;
475
seen_objects = g_hash_table_new (direct_hash_no_null, g_direct_equal);
477
g_hash_table_insert (seen_objects, proc, GINT_TO_POINTER (1));
480
match->children = node_add_trace (info->profile, info->profile_objects, match->children, process,
481
trace, size, seen_objects);
483
g_hash_table_destroy (seen_objects);
487
link_parents (Node *node, Node *parent)
492
node->parent = parent;
494
link_parents (node->siblings, parent);
495
link_parents (node->children, node);
499
compute_object_total (gpointer key, gpointer value, gpointer data)
502
ProfileObject *object = key;
504
for (node = value; node != NULL; node = node->next)
506
object->self += node->self;
508
object->total += node->total;
513
225
profile_new (StackStash *stash)
517
info.profile = g_new (Profile, 1);
518
info.profile->call_tree = NULL;
519
info.profile->nodes_by_object =
520
g_hash_table_new (direct_hash_no_null, g_direct_equal);
521
info.profile->size = 0;
523
/* profile objects */
524
info.profile_objects = g_hash_table_new_full (g_str_hash, g_str_equal,
527
stack_stash_foreach (stash, generate_object_table, &info);
528
stack_stash_foreach (stash, generate_call_tree, &info);
529
link_parents (info.profile->call_tree, NULL);
531
g_hash_table_foreach (info.profile->nodes_by_object, compute_object_total, NULL);
533
g_hash_table_destroy (info.profile_objects);
227
Profile *profile = g_new (Profile, 1);
229
profile->stash = stack_stash_ref (stash);
539
add_trace_to_tree (ProfileDescendant **tree, GList *trace, guint size)
235
add_trace_to_tree (StackLink *trace, gint size, gpointer data)
542
GPtrArray *nodes_to_unmark = g_ptr_array_new ();
543
GPtrArray *nodes_to_unmark_recursive = g_ptr_array_new ();
544
238
ProfileDescendant *parent = NULL;
547
GPtrArray *seen_objects = g_ptr_array_new ();
549
for (list = trace; list != NULL; list = list->next)
239
ProfileDescendant **tree = data;
245
for (; link != NULL; link = link->prev)
551
Node *node = list->data;
247
gpointer address = U64_TO_POINTER (link->data);
248
ProfileDescendant *prev = NULL;
552
249
ProfileDescendant *match = NULL;
556
for (match = *tree; match != NULL; match = match->siblings)
251
for (match = *tree; match != NULL; prev = match, match = match->siblings)
558
if (match->object == node->object)
253
if (match->name == address)
258
prev->siblings = match->siblings;
259
match->siblings = *tree;
564
ProfileDescendant *seen_tree_node;
566
268
/* Have we seen this object further up the tree? */
567
seen_tree_node = NULL;
568
for (i = 0; i < seen_objects->len; ++i)
570
ProfileDescendant *n = seen_objects->pdata[i];
571
if (n->object == node->object)
577
ProfileDescendant *node;
581
for (node = parent; node != seen_tree_node->parent; node = node->parent)
583
node->non_recursion -= size;
584
--node->marked_non_recursive;
587
match = seen_tree_node;
589
g_ptr_array_remove_range (seen_objects, 0, seen_objects->len);
590
for (node = match; node != NULL; node = node->parent)
591
g_ptr_array_add (seen_objects, node);
269
for (match = parent; match != NULL; match = match->parent)
271
if (match->name == address)
597
278
match = g_new (ProfileDescendant, 1);
599
match->object = node->object;
600
match->non_recursion = 0;
280
match->name = address;
281
match->cumulative = 0;
603
283
match->children = NULL;
604
match->marked_non_recursive = 0;
605
match->marked_total = FALSE;
606
284
match->parent = parent;
608
285
match->siblings = *tree;
612
if (!match->marked_non_recursive)
614
g_ptr_array_add (nodes_to_unmark, match);
615
match->non_recursion += size;
616
++match->marked_non_recursive;
619
if (!match->marked_total)
621
g_ptr_array_add (nodes_to_unmark_recursive, match);
623
match->total += size;
624
match->marked_total = TRUE;
630
g_ptr_array_add (seen_objects, match);
632
289
tree = &(match->children);
636
g_ptr_array_free (seen_objects, TRUE);
638
len = nodes_to_unmark->len;
639
for (i = 0; i < len; ++i)
641
ProfileDescendant *tree_node = nodes_to_unmark->pdata[i];
643
tree_node->marked_non_recursive = 0;
646
len = nodes_to_unmark_recursive->len;
647
for (i = 0; i < len; ++i)
649
ProfileDescendant *tree_node = nodes_to_unmark_recursive->pdata[i];
651
tree_node->marked_total = FALSE;
654
g_ptr_array_free (nodes_to_unmark, TRUE);
655
g_ptr_array_free (nodes_to_unmark_recursive, TRUE);
659
node_list_leaves (Node *node, GList **leaves)
664
*leaves = g_list_prepend (*leaves, node);
666
for (n = node->children; n != NULL; n = n->siblings)
667
node_list_leaves (n, leaves);
671
add_leaf_to_tree (ProfileDescendant **tree, Node *leaf, Node *top)
676
for (node = leaf; node != top->parent; node = node->parent)
677
trace = g_list_prepend (trace, node);
679
add_trace_to_tree (tree, trace, leaf->self);
293
parent->self += size;
296
parent->cumulative += size;
297
parent = parent->parent;
684
301
ProfileDescendant *
685
profile_create_descendants (Profile *profile, ProfileObject *object)
302
profile_create_descendants (Profile *profile,
687
305
ProfileDescendant *tree = NULL;
690
node = g_hash_table_lookup (profile->nodes_by_object, object);
307
StackNode *node = stack_stash_find_node (profile->stash, object_name);
695
311
if (node->toplevel)
697
GList *leaves = NULL;
700
node_list_leaves (node, &leaves);
702
for (list = leaves; list != NULL; list = list->next)
703
add_leaf_to_tree (&tree, list->data, node);
705
g_list_free (leaves);
312
stack_node_foreach_trace (node, add_trace_to_tree, &tree);
707
314
node = node->next;
714
321
profile_caller_new (void)
716
323
ProfileCaller *caller = g_new (ProfileCaller, 1);
717
325
caller->next = NULL;
718
326
caller->self = 0;
719
327
caller->total = 0;
724
profile_list_callers (Profile *profile,
725
ProfileObject *callee)
333
profile_list_callers (Profile *profile,
729
GHashTable *callers_by_object;
730
GHashTable *seen_callers;
338
GHashTable *callers_by_name;
339
GHashTable *processed_callers;
731
340
ProfileCaller *result = NULL;
734
g_hash_table_new (g_direct_hash, g_direct_equal);
735
seen_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
737
callee_node = g_hash_table_lookup (profile->nodes_by_object, callee);
739
for (node = callee_node; node; node = node->next)
342
callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal);
343
processed_callers = g_hash_table_new (g_direct_hash, g_direct_equal);
345
callees = stack_stash_find_node (profile->stash, callee_name);
347
for (node = callees; node != NULL; node = node->next)
741
ProfileObject *object;
743
object = node->parent->object;
747
if (!g_hash_table_lookup (callers_by_object, object))
349
ProfileCaller *caller;
354
caller = g_hash_table_lookup (
355
callers_by_name, U64_TO_POINTER (node->parent->data));
749
ProfileCaller *caller = profile_caller_new ();
750
caller->object = object;
751
g_hash_table_insert (callers_by_object, object, caller);
359
caller = profile_caller_new ();
360
caller->name = U64_TO_POINTER (node->parent->data);
753
364
caller->next = result;
367
g_hash_table_insert (
368
callers_by_name, U64_TO_POINTER (node->parent->data), caller);
758
for (node = callee_node; node != NULL; node = node->next)
372
for (node = callees; node != NULL; node = node->next)
374
StackNode *top_caller = node->parent;
375
StackNode *top_callee = node;
763
377
ProfileCaller *caller;
764
ProfileObject *object;
767
object = node->parent->object;
771
caller = g_hash_table_lookup (callers_by_object, object);
773
/* find topmost node/parent pair identical to this node/parent */
774
top_caller = node->parent;
776
382
for (n = node; n && n->parent; n = n->parent)
778
if (n->object == node->object &&
779
n->parent->object == node->parent->object)
384
if (n->data == node->data &&
385
n->parent->data == node->parent->data)
781
387
top_caller = n->parent;
392
caller = g_hash_table_lookup (
393
callers_by_name, U64_TO_POINTER (node->parent->data));
786
if (!g_hash_table_lookup (seen_callers, top_caller))
395
if (!g_hash_table_lookup (processed_callers, top_caller))
788
397
caller->total += top_callee->total;
790
g_hash_table_insert (seen_callers, top_caller, (void *)0x1);
399
g_hash_table_insert (processed_callers, top_caller, top_caller);
794
caller->self += node->self;
797
g_hash_table_destroy (seen_callers);
798
g_hash_table_destroy (callers_by_object);
805
node_free (Node *node)
810
node_free (node->siblings);
811
node_free (node->children);
816
free_object (gpointer key, gpointer value, gpointer data)
818
profile_object_free (key);
402
caller->self += node->size;
405
g_hash_table_destroy (processed_callers);
406
g_hash_table_destroy (callers_by_name);
412
/* This code generates a list of all ancestors, rather than
413
* all callers. It turned out to not work well in practice,
414
* but on the other hand the single list of callers is not
415
* all that great either, so we'll keep it around commented
420
add_to_list (gpointer key,
424
GList **list = user_data;
426
*list = g_list_prepend (*list, value);
430
listify_hash_table (GHashTable *hash_table)
432
GList *result = NULL;
434
g_hash_table_foreach (hash_table, add_to_list, &result);
440
profile_list_ancestors (Profile *profile,
445
GHashTable *callers_by_name;
446
ProfileCaller *result = NULL;
448
callers_by_name = g_hash_table_new (g_direct_hash, g_direct_equal);
450
callees = stack_stash_find_node (profile->stash, callee_name);
452
for (node = callees; node != NULL; node = node->next)
455
gboolean seen_recursive_call;
456
GHashTable *total_ancestors;
457
GHashTable *all_ancestors;
460
/* Build a list of those ancestors that should get assigned
461
* totals. If this callee does not have any recursive calls
462
* higher up, that means all of it's ancestors. If it does
463
* have a recursive call, only the one between this node
464
* and the recursive call should get assigned total
466
seen_recursive_call = FALSE;
467
all_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal);
468
total_ancestors = g_hash_table_new (g_direct_hash, g_direct_equal);
469
for (n = node->parent; n; n = n->parent)
471
if (!seen_recursive_call)
473
g_hash_table_insert (total_ancestors, n->address, n);
477
g_hash_table_remove (total_ancestors, n->address);
480
g_hash_table_insert (all_ancestors, n->address, n);
482
if (n->address == node->address)
483
seen_recursive_call = TRUE;
486
all = listify_hash_table (all_ancestors);
488
for (list = all; list; list = list->next)
490
ProfileCaller *caller;
491
StackNode *ancestor = list->data;
493
caller = g_hash_table_lookup (callers_by_name, ancestor->address);
497
caller = profile_caller_new ();
498
g_hash_table_insert (
499
callers_by_name, ancestor->address, caller);
500
caller->name = ancestor->address;
502
caller->next = result;
506
caller->self += node->size;
508
if (g_hash_table_lookup (total_ancestors, ancestor->address))
509
caller->total += node->total;
513
g_hash_table_destroy (all_ancestors);
514
g_hash_table_destroy (total_ancestors);
822
522
profile_free (Profile *profile)
824
g_hash_table_foreach (profile->nodes_by_object, free_object, NULL);
826
node_free (profile->call_tree);
828
g_hash_table_destroy (profile->nodes_by_object);
524
stack_stash_unref (profile->stash);
830
525
g_free (profile);
834
profile_descendant_free (ProfileDescendant *descendant)
529
profile_descendant_free (ProfileDescendant *descendant)