1 /* MemProf -- memory profiler and leak detector
2 * Copyright 2002, Soeren Sandmann
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include <glib/gi18n.h>
27 block_create_stack_list (Block
*block
, MPProcess
*process
, GHashTable
*skip_hash
)
32 for (element
= block
->stack
; element
!= NULL
; element
= element
->parent
)
34 const char *symbol
= process_locate_symbol (process
, (guint
)element
->address
);
36 if (symbol
&& g_hash_table_lookup (skip_hash
, symbol
))
39 stack
= g_list_prepend (stack
, element
);
46 profile_add_stack_trace (Profile
*profile
, GList
*stack
, guint size
)
49 GPtrArray
*roots
= profile
->roots
;
50 ProfileNode
*parent
= NULL
;
51 GHashTable
*seen_symbols
= g_hash_table_new_full (symbol_hash
, symbol_equal
,
52 (GDestroyNotify
)symbol_free
, NULL
);
54 for (list
= stack
; list
!= NULL
; list
= list
->next
)
56 StackNode
*element
= list
->data
;
57 ProfileNode
*match
= NULL
;
59 process_locate_symbol (profile
->process
, GPOINTER_TO_SIZE(element
->address
));
62 for (i
= 0; i
< roots
->len
; ++i
)
64 ProfileNode
*node
= roots
->pdata
[i
];
66 if (symbol_equal (node
->symbol
, symbol
))
72 ProfileNode
*next_node
;
74 match
= g_new (ProfileNode
, 1);
76 match
->symbol
= symbol_copy (symbol
);
80 if (g_hash_table_lookup (seen_symbols
, symbol
))
81 match
->toplevel
= FALSE
;
83 match
->toplevel
= TRUE
;
85 next_node
= g_hash_table_lookup (profile
->nodes_by_symbol
, symbol
);
87 match
->next
= next_node
;
90 g_hash_table_insert (profile
->nodes_by_symbol
, symbol_copy (symbol
), match
);
92 match
->children
= g_ptr_array_new ();
93 match
->parent
= parent
;
95 g_ptr_array_add (roots
, match
);
98 g_hash_table_insert (seen_symbols
, symbol_copy (symbol
), GINT_TO_POINTER (1));
100 match
->total
+= size
;
105 roots
= match
->children
;
108 g_hash_table_destroy (seen_symbols
);
112 profile_build_tree (Profile
*profile
, GList
*blocks
, GSList
*skip_funcs
)
114 GHashTable
*skip_hash
= g_hash_table_new (g_str_hash
, g_str_equal
);
118 for (slist
= skip_funcs
; slist
!= NULL
; slist
= slist
->next
)
119 g_hash_table_insert (skip_hash
, slist
->data
, "");
121 for (list
= blocks
; list
!= NULL
; list
= list
->next
)
123 Block
*block
= list
->data
;
124 GList
*stack
= block_create_stack_list (block
, profile
->process
, skip_hash
);
126 profile_add_stack_trace (profile
, stack
, block
->size
);
131 g_hash_table_destroy (skip_hash
);
135 add_function (gpointer key
, gpointer value
, gpointer data
)
139 Profile
*profile
= data
;
143 func
= g_new (ProfileFunc
, 1);
150 func
->self
+= node
->self
;
152 func
->total
+= node
->total
;
157 g_ptr_array_add (profile
->functions
, func
);
161 add_block_to_list (Block
*block
, gpointer data
)
163 GList
**blocks
= data
;
165 *blocks
= g_list_prepend (*blocks
, block
);
169 profile_create (MPProcess
*process
, GSList
*skip_funcs
)
172 GList
*blocks
= NULL
;
175 process_block_foreach (process
, add_block_to_list
, &blocks
);
177 profile
= g_new (Profile
, 1);
178 profile
->roots
= g_ptr_array_new ();
179 profile
->functions
= g_ptr_array_new ();
180 profile
->n_bytes
= 0;
181 profile
->process
= process
;
182 profile
->nodes_by_symbol
= g_hash_table_new_full (symbol_hash
, symbol_equal
,
183 (GDestroyNotify
)symbol_free
, NULL
);
185 profile_build_tree (profile
, blocks
, skip_funcs
);
187 for (list
= blocks
; list
!= NULL
; list
= list
->next
)
189 Block
*block
= list
->data
;
191 profile
->n_bytes
+= block
->size
;
194 g_hash_table_foreach (profile
->nodes_by_symbol
, add_function
, profile
);
196 g_list_free (blocks
);
201 profile_node_free (ProfileNode
*node
)
205 for (i
= 0; i
< node
->children
->len
; ++i
)
206 profile_node_free (node
->children
->pdata
[i
]);
208 symbol_free (node
->symbol
);
209 g_ptr_array_free (node
->children
, TRUE
);
214 profile_free (Profile
*profile
)
218 for (i
= 0; i
< profile
->roots
->len
; ++i
)
219 profile_node_free (profile
->roots
->pdata
[i
]);
220 g_ptr_array_free (profile
->roots
, TRUE
);
222 for (i
= 0; i
< profile
->functions
->len
; ++i
)
223 g_free (profile
->functions
->pdata
[i
]);
224 g_ptr_array_free (profile
->functions
, TRUE
);
226 g_hash_table_destroy (profile
->nodes_by_symbol
);
231 node_list_leaves (ProfileNode
*node
, GList
**leaves
)
236 *leaves
= g_list_prepend (*leaves
, node
);
238 for (i
= 0; i
< node
->children
->len
; ++i
)
239 node_list_leaves (node
->children
->pdata
[i
], leaves
);
243 add_trace_to_tree (GPtrArray
*roots
, GList
*trace
, guint size
)
246 GList
*nodes_to_unmark
= NULL
;
247 GList
*nodes_to_unmark_recursive
= NULL
;
248 ProfileDescendantTreeNode
*parent
= NULL
;
250 GHashTable
*seen_symbols
= g_hash_table_new_full (symbol_hash
, symbol_equal
,
251 (GDestroyNotify
)symbol_free
, NULL
);
253 for (list
= trace
; list
!= NULL
; list
= list
->next
)
256 ProfileNode
*node
= list
->data
;
257 ProfileDescendantTreeNode
*match
= NULL
;
259 for (i
= 0; i
< roots
->len
; ++i
)
261 ProfileDescendantTreeNode
*tree_node
= roots
->pdata
[i
];
263 if (symbol_equal (tree_node
->symbol
, node
->symbol
))
269 ProfileDescendantTreeNode
*seen_tree_node
;
271 seen_tree_node
= g_hash_table_lookup (seen_symbols
, node
->symbol
);
275 ProfileDescendantTreeNode
*node
;
279 for (node
= parent
; node
!= seen_tree_node
->parent
; node
= node
->parent
)
281 node
->non_recursion
-= size
;
282 --node
->marked_non_recursive
;
285 match
= seen_tree_node
;
287 g_hash_table_destroy (seen_symbols
);
288 seen_symbols
= g_hash_table_new_full (symbol_hash
, symbol_equal
,
289 (GDestroyNotify
)symbol_free
, NULL
);
291 for (node
= match
; node
!= NULL
; node
= node
->parent
)
292 g_hash_table_insert (seen_symbols
, symbol_copy (node
->symbol
), node
);
299 match
= g_new (ProfileDescendantTreeNode
, 1);
301 match
->symbol
= symbol_copy (node
->symbol
);
302 match
->non_recursion
= 0;
305 match
->children
= g_ptr_array_new ();
306 match
->marked_non_recursive
= 0;
307 match
->marked_total
= FALSE
;
308 match
->parent
= parent
;
310 g_ptr_array_add (roots
, match
);
313 if (!match
->marked_non_recursive
)
315 nodes_to_unmark
= g_list_prepend (nodes_to_unmark
, match
);
316 match
->non_recursion
+= size
;
317 ++match
->marked_non_recursive
;
320 if (!match
->marked_total
)
322 nodes_to_unmark_recursive
= g_list_prepend (
323 nodes_to_unmark_recursive
, match
);
325 match
->total
+= size
;
326 match
->marked_total
= TRUE
;
332 g_hash_table_insert (seen_symbols
, symbol_copy (node
->symbol
), match
);
334 roots
= match
->children
;
338 g_hash_table_destroy (seen_symbols
);
340 for (list
= nodes_to_unmark
; list
!= NULL
; list
= list
->next
)
342 ProfileDescendantTreeNode
*tree_node
= list
->data
;
344 tree_node
->marked_non_recursive
= 0;
347 for (list
= nodes_to_unmark_recursive
; list
!= NULL
; list
= list
->next
)
349 ProfileDescendantTreeNode
*tree_node
= list
->data
;
351 tree_node
->marked_total
= FALSE
;
354 g_list_free (nodes_to_unmark
);
358 add_leaf_to_tree (ProfileDescendantTree
*tree
, ProfileNode
*leaf
, ProfileNode
*top
)
365 * XXX: FIXME: node->parent should always lead to a valid frame
366 * but it does not and more work needs to be done to figure out
367 * why this is the case. This can be easily reproduced with the
368 * GtkLauncher of WebKit/GTK+.
370 for (node
= leaf
; node
&& node
!= top
->parent
; node
= node
->parent
)
371 trace
= g_list_prepend (trace
, node
);
373 add_trace_to_tree (tree
->roots
, trace
, leaf
->self
);
378 ProfileDescendantTree
*
379 profile_func_create_descendant_tree (ProfileFunc
*func
)
381 ProfileDescendantTree
*tree
= g_new (ProfileDescendantTree
, 1);
384 tree
->roots
= g_ptr_array_new ();
386 for (node
= func
->node
; node
!= NULL
; node
= node
->next
)
389 GList
*leaves
= NULL
;
392 node_list_leaves (node
, &leaves
);
394 for (list
= leaves
; list
!= NULL
; list
= list
->next
)
395 add_leaf_to_tree (tree
, list
->data
, node
);
397 g_list_free (leaves
);
404 profile_descendant_tree_node_free (ProfileDescendantTreeNode
*node
)
408 for (i
= 0; i
< node
->children
->len
; ++i
)
410 ProfileDescendantTreeNode
*child
= node
->children
->pdata
[i
];
412 profile_descendant_tree_node_free (child
);
415 g_ptr_array_free (node
->children
, TRUE
);
420 profile_descendant_tree_free (ProfileDescendantTree
*descendant_tree
)
424 for (i
= 0; i
< descendant_tree
->roots
->len
; ++i
)
426 ProfileDescendantTreeNode
*node
= descendant_tree
->roots
->pdata
[i
];
428 profile_descendant_tree_node_free (node
);
430 g_ptr_array_free (descendant_tree
->roots
, TRUE
);
431 g_free (descendant_tree
);
435 profile_func_create_caller_list (ProfileFunc
*func
)
437 GPtrArray
*result
= g_ptr_array_new ();
438 GHashTable
*callers_by_symbol
= g_hash_table_new_full (
439 symbol_hash
, symbol_equal
,
440 (GDestroyNotify
)symbol_free
, NULL
);
441 GHashTable
*marked_callers
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
442 ProfileFunc
*spontaneous
= NULL
;
445 for (node
= func
->node
; node
!= NULL
; node
= node
->next
)
449 if (!g_hash_table_lookup (callers_by_symbol
, node
->parent
->symbol
))
451 ProfileFunc
*caller
= g_new (ProfileFunc
, 1);
455 caller
->node
= node
->parent
;
457 g_hash_table_insert (
458 callers_by_symbol
, symbol_copy (node
->parent
->symbol
), caller
);
459 g_ptr_array_add (result
, caller
);
466 spontaneous
= g_new (ProfileFunc
, 1);
467 spontaneous
->total
= 0;
468 spontaneous
->self
= 0;
469 spontaneous
->node
= NULL
;
470 g_ptr_array_add (result
, spontaneous
);
475 for (node
= func
->node
; node
!= NULL
; node
= node
->next
)
477 ProfileNode
*top_caller_node
;
478 ProfileNode
*top_callee_node
;
484 g_assert (spontaneous
);
485 caller
= spontaneous
;
488 caller
= g_hash_table_lookup (callers_by_symbol
, node
->parent
->symbol
);
490 /* find topmost node/parent pair identical to this node/parent */
491 top_caller_node
= node
->parent
;
492 top_callee_node
= node
;
493 for (n
= node
->parent
; n
&& n
->parent
!= NULL
; n
= n
->parent
)
495 if (symbol_equal (n
->symbol
, node
->symbol
) &&
496 symbol_equal (n
->parent
->symbol
, top_caller_node
->symbol
))
498 top_caller_node
= n
->parent
;
502 if (!g_hash_table_lookup (marked_callers
, top_caller_node
))
504 caller
->total
+= top_callee_node
->total
;
506 g_hash_table_insert (marked_callers
, top_caller_node
, GINT_TO_POINTER (1));
510 caller
->self
+= node
->self
;
513 g_hash_table_destroy (marked_callers
);
514 g_hash_table_destroy (callers_by_symbol
);
520 profile_caller_list_free (GPtrArray
*caller_list
)
524 for (i
= 0; i
< caller_list
->len
; ++i
)
525 g_free (caller_list
->pdata
[i
]);
527 g_ptr_array_free (caller_list
, TRUE
);
531 compare_profile_funcs (gconstpointer a
, gconstpointer b
)
533 const ProfileFunc
*pa
= * (const ProfileFunc
**) a
;
534 const ProfileFunc
*pb
= * (const ProfileFunc
**) b
;
535 if (pa
->total
> pb
->total
)
537 if (pa
->total
< pb
->total
)
543 create_sorted_profile_funcs (GPtrArray
*funcs
)
546 GPtrArray
*functions
;
548 functions
= g_ptr_array_sized_new (funcs
->len
);
550 for (i
= 0; i
< funcs
->len
; ++i
)
551 g_ptr_array_add (functions
, funcs
->pdata
[i
]);
553 g_ptr_array_sort (functions
, compare_profile_funcs
);
559 compare_descendant_tree_nodes (gconstpointer a
, gconstpointer b
)
561 const ProfileDescendantTreeNode
*pa
= * (const ProfileDescendantTreeNode
**) a
;
562 const ProfileDescendantTreeNode
*pb
= * (const ProfileDescendantTreeNode
**) b
;
563 if (pa
->total
> pb
->total
)
565 if (pa
->total
< pb
->total
)
571 create_sorted_descendant_tree_nodes (GPtrArray
*funcs
)
574 GPtrArray
*functions
;
576 functions
= g_ptr_array_sized_new (funcs
->len
);
578 for (i
= 0; i
< funcs
->len
; ++i
)
579 g_ptr_array_add (functions
, funcs
->pdata
[i
]);
581 g_ptr_array_sort (functions
, compare_descendant_tree_nodes
);
587 output_callers (FILE* out
, ProfileFunc
*func
)
590 GPtrArray
*profile_callers
, *callers
;
592 profile_callers
= profile_func_create_caller_list (func
);
593 callers
= create_sorted_profile_funcs (profile_callers
);
595 fprintf (out
, " Callers (with count) that contribute at least for 1%%:\n");
597 for (i
= 0; i
< callers
->len
; ++i
) {
599 unsigned int percent
;
600 ProfileFunc
*caller
= callers
->pdata
[i
];
603 if (caller
->node
->symbol
)
604 name
= caller
->node
->symbol
;
609 name
= "<spontaneous>";
610 percent
= (caller
->total
* 100)/ func
->total
;
613 fprintf (out
, " %10d %10d %3d %% %s\n",
614 caller
->self
, caller
->total
, percent
, name
);
617 profile_caller_list_free (profile_callers
);
618 g_ptr_array_free (callers
, 1);
622 output_descendants (FILE* out
, ProfileFunc
*func
)
625 ProfileDescendantTree
*descendant_tree
;
626 ProfileDescendantTreeNode
*node
;
629 descendant_tree
= profile_func_create_descendant_tree (func
);
630 node
= descendant_tree
->roots
->pdata
[0];
631 children
= create_sorted_descendant_tree_nodes (node
->children
);
633 fprintf (out
, " Descendants (with count) that contribute at least for 1%%:\n");
635 for (i
= 0; i
< children
->len
; ++i
) {
637 unsigned int percent
;
638 ProfileDescendantTreeNode
*child
= children
->pdata
[i
];
641 name
= child
->symbol
;
645 percent
= (child
->total
* 100)/ node
->total
;
648 fprintf (out
, " %10d %10d %3d %% %s\n",
649 child
->self
, child
->total
, percent
, name
);
652 profile_descendant_tree_free (descendant_tree
);
653 g_ptr_array_free (children
, 1);
657 output_profile_summary (FILE *out
, guint n_bytes
, GPtrArray
*functions
)
661 fprintf (out
, " self total total %% symbol\n");
662 fprintf (out
, " --------- ---------- ------- ------------\n");
664 for (i
= 0; i
< functions
->len
; ++i
) {
666 ProfileFunc
*func
= functions
->pdata
[i
];
667 if (func
->node
->symbol
)
668 name
= func
->node
->symbol
;
671 fprintf (out
, "%10d %10d %5.2f %% %s\n",
672 func
->self
, func
->total
,
673 func
->total
* 100.0 / (double) n_bytes
,
679 output_profile_details (FILE *out
, guint n_bytes
, GPtrArray
*functions
)
683 for (i
= 0; i
< functions
->len
; ++i
) {
685 ProfileFunc
*func
= functions
->pdata
[i
];
686 if (func
->node
->symbol
)
687 name
= func
->node
->symbol
;
690 fprintf (out
, "########################\n");
691 fprintf (out
, "%10d %10d %5.2f %% %s\n",
692 func
->self
, func
->total
,
693 func
->total
* 100.0 / (double) n_bytes
,
695 output_callers (out
, func
);
696 output_descendants (out
, func
);
701 profile_write (Profile
*profile
, const gchar
*outfile
)
704 GPtrArray
*functions
;
706 out
= fopen (outfile
, "w");
708 show_error (NULL
, ERROR_MODAL
, _("Cannot open output file: %s\n"),
713 functions
= create_sorted_profile_funcs (profile
->functions
);
715 fprintf (out
, "Total number of bytes profiled: %u\n", profile
->n_bytes
);
717 output_profile_summary (out
, profile
->n_bytes
, functions
);
718 output_profile_details (out
, profile
->n_bytes
, functions
);
720 g_ptr_array_free (functions
, 1);