1 /* -*- mode: C; c-file-style: "linux" -*- */
3 /* MemProf -- memory profiler and leak detector
4 * Copyright 1999, 2000, 2001, Red Hat, Inc.
5 * Copyright 2002, Kristian Rietveld
6 * Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk)
7 * Copyright 2009, 2010, Holger Hans Peter Fryther
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <glade/glade.h>
42 #include "treeviewutils.h"
45 #include <glib/gi18n.h>
46 #include <glib/gprintf.h>
47 #include "elfparser.h"
65 PROFILE_DESCENDANTS_NAME
,
66 PROFILE_DESCENDANTS_SELF
,
67 PROFILE_DESCENDANTS_NONRECURSE
,
68 PROFILE_DESCENDANTS_SYMBOL
86 MPServer
*global_server
;
88 static ProcessWindow
*process_window_new (void);
89 static void process_window_destroy (ProcessWindow
*pwin
);
90 static void process_window_reset (ProcessWindow
*pwin
);
92 static GSList
*skip_funcs
= NULL
;
93 static GSList
*skip_regexes
= NULL
;
95 static gboolean default_follow_fork
= FALSE
;
96 static gboolean default_follow_exec
= FALSE
;
98 char *stack_command
= NULL
;
100 GSList
*process_windows
= NULL
;
102 static MPProfileType profile_type
;
105 /* Helper function to print a clear error message if there
106 * is a problem looking up a particular widget
109 get_widget (GladeXML
*glade
,
112 GtkWidget
*widget
= glade_xml_get_widget (glade
, name
);
114 g_error ("Cannot lookup %s\n", name
);
120 /************************************************************
122 ************************************************************/
125 draw_bar (GtkWidget
*widget
, int red
, int green
, int blue
,
128 GdkGC
*gc
= gdk_gc_new (widget
->window
);
135 gdk_gc_set_rgb_fg_color (gc
, &color
);
137 gdk_draw_rectangle (widget
->window
, gc
, TRUE
,
138 0, 0, size
, widget
->allocation
.height
);
144 gdk_gc_set_rgb_fg_color (gc
, &color
);
146 gdk_draw_rectangle (widget
->window
, gc
, FALSE
,
147 0, 0, size
- 1, widget
->allocation
.height
- 1);
153 on_usage_area_expose (GtkWidget
*widget
, GdkEvent
*event
,
156 ProcessWindow
*pwin
= data
;
163 width
= widget
->allocation
.width
;
166 draw_bar (widget
, 0xffff, 0xffff, 0xffff, width
);
170 bytes_used
= pwin
->process
->bytes_used
;
175 leak_size
= (width
* ((double)pwin
->usage_leaked
/ pwin
->usage_max
));
176 high_size
= (width
* ((double)pwin
->usage_high
/ pwin
->usage_max
));
177 current_size
= (width
* ((double)bytes_used
/ pwin
->usage_max
));
179 draw_bar (widget
, 0x0000, 0x0000, 0xffff, high_size
);
180 draw_bar (widget
, 0xffff, 0xffff, 0x0000, current_size
);
181 draw_bar (widget
, 0xffff, 0x0000, 0x0000, leak_size
);
187 update_status (gpointer data
)
190 ProcessWindow
*pwin
= (ProcessWindow
*)data
;
192 tmp
= g_strdup_printf ("%d", pwin
->process
->bytes_used
);
193 gtk_label_set_text (GTK_LABEL (pwin
->total_bytes_label
), tmp
);
196 tmp
= g_strdup_printf ("%d", pwin
->process
->n_allocations
);
197 gtk_label_set_text (GTK_LABEL (pwin
->n_allocations_label
), tmp
);
200 tmp
= g_strdup_printf ("%d samples", pwin
->process
->bytes_used
);
201 gtk_label_set_text (GTK_LABEL (pwin
->profile_status_label
), tmp
);
204 if (pwin
->process
->n_allocations
== 0)
207 tmp
= g_strdup_printf ("%.2f",
208 (double)pwin
->process
->bytes_used
/
209 pwin
->process
->n_allocations
);
210 gtk_label_set_text (GTK_LABEL (pwin
->bytes_per_label
), tmp
);
213 if (pwin
->process
->bytes_used
> pwin
->usage_max
) {
214 while ((pwin
->process
->bytes_used
> pwin
->usage_max
))
215 pwin
->usage_max
*= 2;
217 tmp
= g_strdup_printf ("%dk", pwin
->usage_max
/ 1024);
218 gtk_label_set_text (GTK_LABEL (pwin
->usage_max_label
), tmp
);
222 pwin
->usage_high
= MAX (pwin
->process
->bytes_used
, pwin
->usage_high
);
224 gtk_widget_queue_draw (pwin
->usage_area
);
231 /************************************************************
233 ************************************************************/
236 get_sort_info (GtkTreeView
*view
, int *sort_column
, GtkSortType
*sort_type
)
238 GtkTreeModel
*model
= gtk_tree_view_get_model (view
);
240 if (model
&& GTK_IS_TREE_SORTABLE (model
))
241 return gtk_tree_sortable_get_sort_column_id (
242 GTK_TREE_SORTABLE (model
), sort_column
, sort_type
);
248 profile_func_list_goto_symbol (ProcessWindow
*pwin
, char *symbol
)
250 GtkTreeModel
*function_list
;
252 gboolean found
= FALSE
;
254 function_list
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
256 if (gtk_tree_model_get_iter_first (function_list
, &iter
)) {
260 gtk_tree_model_get (function_list
, &iter
,
261 PROFILE_FUNC_FUNC
, &func
,
264 if (symbol_equal (func
->node
->symbol
, symbol
)) {
268 } while (gtk_tree_model_iter_next (function_list
, &iter
));
273 gtk_tree_model_get_path (function_list
, &iter
);
275 gtk_tree_view_set_cursor (
276 GTK_TREE_VIEW (pwin
->profile_func_tree_view
), path
, 0, FALSE
);
278 gtk_widget_grab_focus (GTK_WIDGET (pwin
->profile_func_tree_view
));
282 profile_descendants_row_activated (GtkTreeView
*treeview
,
284 GtkTreeViewColumn
*column
,
287 GtkTreeModel
*descendants_store
;
288 ProcessWindow
*pwin
= data
;
292 descendants_store
= gtk_tree_view_get_model (treeview
);
293 if (!gtk_tree_model_get_iter (descendants_store
, &iter
, path
))
296 gtk_tree_model_get (descendants_store
, &iter
, PROFILE_DESCENDANTS_SYMBOL
, &desc_symbol
, -1);
298 profile_func_list_goto_symbol (pwin
, desc_symbol
);
302 profile_caller_row_activated (GtkTreeView
*treeview
,
304 GtkTreeViewColumn
*column
,
307 GtkTreeModel
*caller_list
;
308 ProcessWindow
*pwin
= data
;
312 caller_list
= gtk_tree_view_get_model (treeview
);
313 if (!gtk_tree_model_get_iter (caller_list
, &iter
, path
))
316 gtk_tree_model_get (caller_list
, &iter
, 3, &caller_symbol
, -1);
318 if (caller_symbol
!= GINT_TO_POINTER (-1))
319 profile_func_list_goto_symbol (pwin
, caller_symbol
);
323 set_double (GtkTreeModel
*model
, GtkTreeIter
*iter
, int column
, double value
)
325 if (GTK_IS_TREE_STORE (model
))
326 gtk_tree_store_set (GTK_TREE_STORE (model
), iter
, column
, value
, -1);
328 gtk_list_store_set (GTK_LIST_STORE (model
), iter
, column
, value
, -1);
332 set_sample (GtkTreeModel
*model
, GtkTreeIter
*iter
, int column
, guint value
, guint n_samples
)
334 if (profile_type
== MP_PROFILE_MEMORY
)
335 set_double (model
, iter
, column
, value
);
337 set_double (model
, iter
, column
, 100 * (double)value
/ n_samples
);
341 add_node (GtkTreeStore
*store
, int n_samples
,
342 const GtkTreeIter
*parent
, ProfileDescendantTreeNode
*node
)
348 g_return_if_fail (GTK_IS_TREE_STORE (store
));
350 gtk_tree_store_insert (store
, &iter
, (GtkTreeIter
*)parent
, 0);
353 name
= elf_demangle (node
->symbol
);
355 name
= g_strdup ("???");
357 if (profile_type
== MP_PROFILE_MEMORY
) {
358 gtk_tree_store_set (store
, &iter
,
359 PROFILE_DESCENDANTS_NAME
, name
,
360 PROFILE_DESCENDANTS_SELF
, (double)node
->self
,
361 PROFILE_DESCENDANTS_NONRECURSE
, (double)node
->non_recursion
,
362 PROFILE_DESCENDANTS_SYMBOL
, node
->symbol
,
366 gtk_tree_store_set (store
, &iter
,
367 PROFILE_DESCENDANTS_NAME
, name
,
368 PROFILE_DESCENDANTS_SELF
, (100.0 * node
->self
) / n_samples
,
369 PROFILE_DESCENDANTS_NONRECURSE
, (100.0 * node
->non_recursion
) / n_samples
,
370 PROFILE_DESCENDANTS_SYMBOL
, node
->symbol
,
376 for (i
= 0; i
< node
->children
->len
; ++i
)
377 add_node (store
, n_samples
, &iter
, node
->children
->pdata
[i
]);
381 profile_selection_changed (GtkTreeSelection
*selection
, ProcessWindow
*pwin
)
384 GtkTreeIter selected
;
386 GtkTreeStore
*tree_store
;
387 GtkListStore
*list_store
;
388 GtkTreeModel
*list_model
;
390 GPtrArray
*caller_list
;
391 ProfileDescendantTree
*descendant_tree
;
395 GtkSortType old_sort_type
;
398 if (!gtk_tree_selection_get_selected (selection
, (GtkTreeModel
**)&store
, &selected
))
400 g_warning ("No selection");
404 n_samples
= pwin
->profile
->n_bytes
;
405 gtk_tree_model_get (GTK_TREE_MODEL (store
), &selected
,
406 PROFILE_FUNC_FUNC
, &func
,
409 was_sorted
= get_sort_info (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
),
410 &old_sort_column
, &old_sort_type
);
412 /* fill descendants tree */
413 tree_store
= gtk_tree_store_new (4,
419 descendant_tree
= profile_func_create_descendant_tree (func
);
421 add_node (tree_store
, n_samples
, NULL
, descendant_tree
->roots
->pdata
[0]);
423 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), TRUE
);
425 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
),
426 GTK_TREE_MODEL (tree_store
));
428 /* Expand the toplevel of the descendant tree so we see the immediate
431 path
= gtk_tree_path_new_from_indices (0, -1);
432 gtk_tree_view_expand_row (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
), path
, FALSE
);
433 gtk_tree_path_free (path
);
436 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store
),
437 old_sort_column
, old_sort_type
);
439 gtk_tree_sortable_set_sort_column_id (
440 GTK_TREE_SORTABLE (tree_store
), 2, GTK_SORT_DESCENDING
);
442 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (tree_store
));
444 g_object_unref (G_OBJECT (tree_store
));
446 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
));
448 profile_descendant_tree_free (descendant_tree
);
450 /* fill caller tree */
451 was_sorted
= get_sort_info (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
),
452 &old_sort_column
, &old_sort_type
);
454 list_store
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_DOUBLE
, G_TYPE_DOUBLE
, G_TYPE_POINTER
);
455 list_model
= GTK_TREE_MODEL (list_store
);
457 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
459 caller_list
= profile_func_create_caller_list (func
);
461 for (i
= 0; i
< caller_list
->len
; ++i
) {
464 ProfileFunc
*caller
= caller_list
->pdata
[i
];
467 if (caller
->node
->symbol
)
468 name
= elf_demangle(caller
->node
->symbol
);
470 name
= g_strdup("???");
473 name
= g_strdup("<spontaneous>");
475 gtk_list_store_append (list_store
, &iter
);
477 gtk_list_store_set (list_store
, &iter
,
478 PROFILE_CALLER_NAME
, name
,
479 PROFILE_CALLER_SYMBOL
, caller
->node
? caller
->node
->symbol
: GINT_TO_POINTER (-1),
482 set_sample (list_model
, &iter
, PROFILE_CALLER_SELF
, caller
->self
, n_samples
);
483 set_sample (list_model
, &iter
, PROFILE_CALLER_TOTAL
, caller
->total
, n_samples
);
487 profile_caller_list_free (caller_list
);
489 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
491 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
),
495 gtk_tree_sortable_set_sort_column_id (
496 GTK_TREE_SORTABLE (list_store
), old_sort_column
, old_sort_type
);
498 gtk_tree_sortable_set_sort_column_id (
499 GTK_TREE_SORTABLE (list_store
), 2, GTK_SORT_DESCENDING
);
501 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (list_store
));
503 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
505 g_object_unref (G_OBJECT (list_store
));
507 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_caller_tree_view
), TRUE
);
511 profile_fill (ProcessWindow
*pwin
)
517 int n_samples
= pwin
->profile
->n_bytes
;
520 GtkSortType old_sort_type
;
524 get_sort_info (GTK_TREE_VIEW (pwin
->profile_func_tree_view
),
525 &old_sort_column
, &old_sort_type
);
527 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), NULL
);
528 store
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_DOUBLE
, G_TYPE_DOUBLE
, G_TYPE_POINTER
);
529 model
= GTK_TREE_MODEL (store
);
531 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), model
);
533 /* inserting in a ListStore is O(n) when sorting ... */
534 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
536 for (i
= 0; i
< pwin
->profile
->functions
->len
; ++i
) {
540 ProfileFunc
*func
= pwin
->profile
->functions
->pdata
[i
];
542 gtk_list_store_append (store
, &iter
);
546 if (func
->node
->symbol
)
547 name
= elf_demangle(func
->node
->symbol
);
549 name
= g_strdup("???");
551 gtk_list_store_set (store
, &iter
,
552 PROFILE_FUNC_NAME
, name
,
553 PROFILE_FUNC_FUNC
, func
,
556 set_sample (model
, &iter
, PROFILE_FUNC_SELF
, func
->self
, n_samples
);
557 set_sample (model
, &iter
, PROFILE_FUNC_TOTAL
, func
->total
, n_samples
);
562 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
565 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store
), old_sort_column
,
569 gtk_tree_sortable_set_sort_column_id (
570 GTK_TREE_SORTABLE (store
), 2, GTK_SORT_DESCENDING
);
573 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (store
));
575 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_func_tree_view
), TRUE
);
577 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
579 g_object_unref (G_OBJECT (store
));
583 /************************************************************
584 * GUI for leak detection
585 ************************************************************/
588 leak_block_get_selected (ProcessWindow
*pwin
)
590 GtkTreeSelection
*selection
;
595 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
596 if (selection
&& gtk_tree_selection_get_selected (selection
, &model
, &iter
))
597 gtk_tree_model_get (model
, &iter
, LEAK_BLOCK_BLOCK
, &block
, -1);
603 leak_block_selection_changed (GtkTreeSelection
*selection
,
606 GtkTreeModel
*model
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
607 GtkListStore
*store
= GTK_LIST_STORE (model
);
608 Block
*block
= leak_block_get_selected (pwin
);
611 gtk_list_store_clear (store
);
616 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
618 for (stack
= block
->stack
; stack
!= NULL
; stack
= stack
->parent
) {
620 const char *filename
;
624 if (!process_find_line (pwin
->process
, stack
->address
,
625 &filename
, &functionname
, &line
)) {
626 /* 0x3f == '?' -- suppress trigraph warnings */
627 functionname
= "(\x3f\x3f\x3f)";
628 filename
= "(\x3f\x3f\x3f)";
632 gtk_list_store_append (store
, &iter
);
633 gtk_list_store_set (store
, &iter
,
634 LEAK_STACK_NAME
, functionname
,
635 LEAK_STACK_LINE
, line
,
636 LEAK_STACK_FILE
, filename
,
640 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
644 leaks_fill (ProcessWindow
*pwin
)
650 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
651 store
= GTK_LIST_STORE (model
);
653 gtk_list_store_clear (store
);
655 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
657 tmp_list
= pwin
->leaks
;
659 const char *filename
;
660 gboolean free_function
= FALSE
;
661 char *functionname
= NULL
;
666 Block
*block
= tmp_list
->data
;
669 for (stack
= block
->stack
; stack
!= NULL
; stack
= stack
->parent
) {
670 if (process_find_line (pwin
->process
, stack
->address
,
671 &filename
, &functionname
, &line
)) {
677 for (tmp_list
= skip_funcs
; tmp_list
!= NULL
; tmp_list
= tmp_list
->next
) {
678 if (!strcmp (functionname
, tmp_list
->data
)) {
687 for (tmp_list
= skip_regexes
; tmp_list
!= NULL
; tmp_list
= tmp_list
->next
) {
690 regcomp (®ex
, tmp_list
->data
, 0);
692 if (!regexec (®ex
, functionname
, 0, NULL
, 0)) {
706 free_function
= TRUE
;
707 functionname
= g_strdup ("(\x3f\x3f\x3f)");
710 gtk_list_store_append (store
, &iter
);
711 gtk_list_store_set (store
, &iter
,
712 LEAK_BLOCK_ADDR
, block
->addr
,
713 LEAK_BLOCK_SIZE
, block
->size
,
714 LEAK_BLOCK_CALLER
, functionname
,
715 LEAK_BLOCK_BLOCK
, block
,
719 g_free (functionname
);
721 tmp_list
= tmp_list
->next
;
724 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
728 leak_stack_run_command (ProcessWindow
*pwin
, Block
*block
, int frame
)
730 const char *filename
;
734 StackNode
*stack
= block
->stack
;
736 stack
= stack
->parent
;
738 if (process_find_line (pwin
->process
, stack
->address
,
739 &filename
, &functionname
, &line
)) {
741 GString
*command
= g_string_new (NULL
);
742 char *p
= stack_command
;
751 g_string_append (command
, filename
);
754 snprintf(buf
, 32, "%d", line
);
755 g_string_append (command
, buf
);
758 g_string_append_c (command
, '%');
761 g_string_append_c (command
, '%');
762 g_string_append_c (command
, *p
);
765 g_string_append_c (command
, *p
);
769 cmdline
= g_strdup_printf ("/bin/sh -c %s", command
->str
);
771 if (!g_spawn_command_line_async (cmdline
, &err
)) {
772 show_error (pwin
->main_window
,
773 ERROR_MODAL
, _("Execution of \"%s\" failed: %s"),
774 command
->str
, err
->message
);
781 g_string_free (command
, FALSE
);
786 leak_stack_row_activated (GtkTreeView
*tree_view
,
796 model
= gtk_tree_view_get_model (tree_view
);
797 gtk_tree_model_get_iter (model
, &iter
, path
);
798 frame
= list_iter_get_index (model
, &iter
);
800 block
= leak_block_get_selected (pwin
);
802 leak_stack_run_command (pwin
, block
, frame
);
807 /************************************************************
808 * File Selection handling
809 ************************************************************/
812 get_filename (const gchar
*title
,
813 const gchar
*suggested_name
)
816 gchar
*filename
= NULL
;
817 dialog
= gtk_file_chooser_dialog_new (title
,
818 NULL
, GTK_FILE_CHOOSER_ACTION_SAVE
,
819 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
820 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
822 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog
), suggested_name
);
823 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
824 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
826 gtk_widget_destroy (dialog
);
830 /* Really ugly utility function to retrieve the ProcessWindow from
831 * either a menu_item or toolbar item.
834 pwin_from_widget (GtkWidget
*widget
)
838 if (GTK_IS_MENU_ITEM (widget
)) {
839 GtkWidget
*menu_shell
= widget
->parent
;
841 while (menu_shell
&& !GTK_IS_MENU_BAR (menu_shell
)) {
842 menu_shell
= gtk_menu_get_attach_widget (GTK_MENU (menu_shell
))->parent
;
844 g_assert (menu_shell
!= NULL
);
846 app
= gtk_widget_get_toplevel (menu_shell
);
848 app
= gtk_widget_get_toplevel (widget
);
850 return g_object_get_data (G_OBJECT (app
), "process-window");
854 close_cb (GtkWidget
*widget
)
856 ProcessWindow
*pwin
= pwin_from_widget (widget
);
858 hide_and_check_quit (pwin
->main_window
);
862 exit_cb (GtkWidget
*widget
)
868 reset_cb (MPProcess
*process
, ProcessWindow
*pwin
)
870 process_window_reset (pwin
);
874 status_changed_cb (MPProcess
*process
, ProcessWindow
*pwin
)
876 if (process
->status
== MP_PROCESS_DEFUNCT
||
877 process
->status
== MP_PROCESS_DETACHED
) {
879 if (g_slist_length (process_windows
) > 1) {
880 tree_window_remove (pwin
);
881 process_window_destroy (pwin
);
883 tree_window_remove (pwin
);
886 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin
->process
), G_CALLBACK (status_changed_cb
), pwin
);
887 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin
->process
), G_CALLBACK (reset_cb
), pwin
);
888 g_object_unref (G_OBJECT (pwin
->process
));
889 pwin
->process
= NULL
;
892 if (pwin
->status_update_timeout
) {
893 g_source_remove (pwin
->status_update_timeout
);
894 pwin
->status_update_timeout
= 0;
897 process_window_reset (pwin
);
901 char *status
= process_get_status_text (process
);
902 char *cmdline
= process_get_cmdline (process
);
903 char *title
= g_strdup_printf ("%s - %s (%d) - %s", _("MemProf"), cmdline
, process
->pid
, status
);
904 gtk_window_set_title (GTK_WINDOW (pwin
->main_window
), title
);
913 list_view_clear (GtkTreeView
*tree_view
)
915 GtkTreeModel
*model
= gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view
));
916 gtk_list_store_clear (GTK_LIST_STORE (model
));
920 process_window_reset (ProcessWindow
*pwin
)
923 profile_free (pwin
->profile
);
924 pwin
->profile
= NULL
;
926 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), NULL
);
927 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
), NULL
);
928 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
), NULL
);
930 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_func_tree_view
), FALSE
);
931 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_caller_tree_view
), FALSE
);
932 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), FALSE
);
936 g_slist_free (pwin
->leaks
);
938 list_view_clear (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
939 list_view_clear (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
942 pwin
->usage_max
= 32*1024;
943 pwin
->usage_high
= 0;
944 pwin
->usage_leaked
= 0;
946 gtk_window_set_title (GTK_WINDOW (pwin
->main_window
), "MemProf");
948 gtk_widget_queue_draw (pwin
->usage_area
);
953 init_process (ProcessWindow
*pwin
, MPProcess
*process
)
955 pwin
->process
= process
;
956 pwin
->draw_memmap
= TRUE
;
958 process_set_follow_fork (pwin
->process
, default_follow_fork
);
959 process_set_follow_exec (pwin
->process
, default_follow_exec
);
961 pwin
->status_update_timeout
=
966 g_signal_connect (process
, "status_changed",
967 G_CALLBACK (status_changed_cb
), pwin
);
968 g_signal_connect (process
, "reset",
969 G_CALLBACK (reset_cb
), pwin
);
971 tree_window_add (pwin
);
975 process_created_cb (MPServer
*server
, MPProcess
*process
)
977 ProcessWindow
*pwin
= process_window_new ();
979 init_process (pwin
, process
);
985 run_file (ProcessWindow
*pwin
, char **args
)
990 g_return_val_if_fail (args
!= NULL
, FALSE
);
991 g_return_val_if_fail (args
[0] != NULL
, FALSE
);
993 path
= process_find_exec (args
);
996 g_warning ("Process new '%s'\n", path
);
997 MPProcess
*process
= process_new (global_server
);
998 process_run (process
, path
, args
);
1000 if (pwin
->process
) {
1001 pwin
= process_window_new ();
1002 tree_window_show ();
1005 init_process (pwin
, process
);
1007 gtk_widget_show (pwin
->main_window
);
1012 show_error (pwin
->main_window
,
1014 _("Cannot find executable for \"%s\""),
1025 run_cb (GtkWidget
*widget
)
1028 GtkWidget
*run_dialog
;
1029 GtkWidget
*filechooser
;
1031 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1033 xml
= glade_xml_new (glade_file
, "RunDialog", NULL
);
1034 run_dialog
= get_widget (xml
, "RunDialog");
1035 filechooser
= get_widget (xml
, "RunDialog-chooser");
1037 g_object_unref (G_OBJECT (xml
));
1040 gtk_window_set_transient_for (GTK_WINDOW (run_dialog
),
1041 GTK_WINDOW (pwin
->main_window
));
1042 if (gtk_dialog_run (GTK_DIALOG (run_dialog
)) == 1) {
1047 text
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser
));
1051 args
= process_parse_exec (text
);
1053 result
= run_file (pwin
, args
);
1065 gtk_widget_destroy (run_dialog
);
1069 kill_cb (GtkWidget
*widget
)
1071 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1074 process_window_maybe_kill (pwin
);
1078 detach_cb (GtkWidget
*widget
)
1080 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1083 process_window_maybe_detach (pwin
);
1087 process_tree_cb (GtkWidget
*widget
)
1089 tree_window_show ();
1093 save_leak_cb (GtkWidget
*widget
)
1095 static gchar
*suggestion
= NULL
;
1098 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1101 filename
= get_filename ("Save Leak Report",
1102 suggestion
? suggestion
: "memprof.leak");
1105 g_free (suggestion
);
1106 suggestion
= filename
;
1108 leaks_print (pwin
->process
, pwin
->leaks
, filename
);
1115 save_profile_cb (GtkWidget
*widget
)
1117 static gchar
*suggestion
= NULL
;
1120 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1122 if (pwin
->profile
) {
1123 filename
= get_filename ("Save Profile",
1124 suggestion
? suggestion
: "memprof.out");
1127 g_free (suggestion
);
1128 suggestion
= filename
;
1130 profile_write (pwin
->profile
, filename
);
1137 save_current_cb (GtkWidget
*widget
)
1139 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1141 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin
->main_notebook
))) {
1143 save_profile_cb (widget
);
1146 save_leak_cb (widget
);
1152 generate_leak_cb (GtkWidget
*widget
)
1155 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1158 if (pwin
->process
) {
1159 process_stop_input (pwin
->process
);
1161 while (pwin
->leaks
) {
1162 block_unref(pwin
->leaks
->data
);
1163 pwin
->leaks
= g_slist_delete_link(pwin
->leaks
, pwin
->leaks
);
1165 pwin
->leaks
= leaks_find (pwin
->process
);
1167 pwin
->usage_leaked
= 0;
1168 tmp_list
= pwin
->leaks
;
1170 pwin
->usage_leaked
+= ((Block
*)tmp_list
->data
)->size
;
1171 tmp_list
= tmp_list
->next
;
1175 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin
->main_notebook
), 1);
1177 process_start_input (pwin
->process
);
1182 generate_profile_cb (GtkWidget
*widget
)
1184 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1186 if (pwin
->process
) {
1187 process_stop_input (pwin
->process
);
1189 if (pwin
->profile
) {
1190 profile_free (pwin
->profile
);
1191 pwin
->profile
= NULL
;
1194 pwin
->profile
= profile_create (pwin
->process
, skip_funcs
);
1195 process_start_input (pwin
->process
);
1196 profile_fill (pwin
);
1198 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin
->main_notebook
), 0);
1203 draw_memmap_toggled_cb (GtkWidget
*widget
)
1205 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1206 pwin
->draw_memmap
= gtk_toggle_tool_button_get_active
1207 (GTK_TOGGLE_TOOL_BUTTON (widget
));
1212 reset_profile_cb (GtkWidget
*widget
)
1214 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1216 process_window_reset (pwin
);
1219 process_clear_input (pwin
->process
);
1223 record_button_toggled_cb (GtkWidget
*widget
)
1225 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1227 if (gtk_toggle_tool_button_get_active
1228 (GTK_TOGGLE_TOOL_BUTTON (widget
)))
1229 process_start_input (pwin
->process
);
1231 process_stop_input (pwin
->process
);
1235 about_cb (GtkWidget
*widget
)
1237 #define OSLASH "\303\270"
1238 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1240 /* FIXME: restore credits */
1241 gtk_show_about_dialog (GTK_WINDOW (pwin
->main_window
),
1247 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1248 "Copyright 2002, Kristian Rietveld\n"
1249 "Copyright 2002, 2006, 2007, S"OSLASH
"ren Sandmann",
1251 "copyright", "Copyright 2004-2006, S"OSLASH
"ren Sandmann",
1253 "version", PACKAGE_VERSION
,
1258 show_error_response (GtkDialog
*dialog
,
1262 if (response_id
== GTK_RESPONSE_OK
)
1263 gtk_widget_destroy (GTK_WIDGET (dialog
));
1267 show_error (GtkWidget
*parent_window
,
1269 const gchar
*format
,
1276 va_start (args
, format
);
1277 g_vasprintf (&message
, format
, args
);
1280 dialog
= gtk_message_dialog_new (parent_window
? GTK_WINDOW (parent_window
) : NULL
,
1281 GTK_DIALOG_DESTROY_WITH_PARENT
,
1282 (error
== ERROR_FATAL
) ?
1284 GTK_MESSAGE_WARNING
,
1285 GTK_BUTTONS_OK
, "%s", message
);
1288 gtk_window_set_title (GTK_WINDOW (dialog
),
1289 (error
== ERROR_FATAL
) ?
1290 _("MemProf Error") : _("MemProf Warning"));
1292 if (error
== ERROR_WARNING
) {
1293 gtk_widget_show (dialog
);
1294 g_signal_connect (dialog
, "response",
1295 G_CALLBACK (show_error_response
), NULL
);
1297 gtk_dialog_run (GTK_DIALOG (dialog
));
1298 gtk_widget_destroy (dialog
);
1301 if (error
== ERROR_FATAL
)
1306 process_window_free (ProcessWindow
*pwin
)
1308 /* FIXME: we leak the process structure */
1311 g_slist_free (pwin
->leaks
);
1314 profile_free (pwin
->profile
);
1316 process_windows
= g_slist_remove (process_windows
, pwin
);
1317 if (!process_windows
)
1325 process_window_destroy (ProcessWindow
*pwin
)
1327 if (pwin
->status_update_timeout
)
1328 g_source_remove (pwin
->status_update_timeout
);
1330 gtk_widget_destroy (pwin
->main_window
);
1334 static GtkTreeViewColumn
*
1335 add_sample_column (GtkTreeView
*view
, const gchar
*title
, gint model_column
)
1339 if (profile_type
== MP_PROFILE_MEMORY
)
1344 return add_double_format_column (view
, title
, model_column
, format
);
1348 setup_profile_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1350 GtkTreeSelection
*selection
;
1351 GtkTreeViewColumn
*col
;
1353 col
= add_plain_text_column (tree_view
, _("Functions"), PROFILE_FUNC_NAME
);
1354 add_sample_column (tree_view
, _("Self"), PROFILE_FUNC_SELF
);
1355 add_sample_column (tree_view
, _("Total"), PROFILE_FUNC_TOTAL
);
1356 gtk_tree_view_column_set_expand (col
, TRUE
);
1358 selection
= gtk_tree_view_get_selection (tree_view
);
1359 g_return_if_fail (selection
!= NULL
);
1360 g_signal_connect (selection
, "changed", G_CALLBACK (profile_selection_changed
), pwin
);
1362 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1366 setup_profile_descendants_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1368 GtkTreeViewColumn
*col
;
1370 col
= add_plain_text_column (tree_view
, _("Descendants"), PROFILE_DESCENDANTS_NAME
);
1371 add_sample_column (tree_view
, _("Self"), PROFILE_DESCENDANTS_SELF
);
1372 add_sample_column (tree_view
, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE
);
1373 gtk_tree_view_column_set_expand (col
, TRUE
);
1375 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), FALSE
);
1377 g_signal_connect (tree_view
, "row-activated",
1378 G_CALLBACK (profile_descendants_row_activated
), pwin
);
1380 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1384 setup_profile_caller_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1386 GtkTreeViewColumn
*col
;
1388 col
= add_plain_text_column (tree_view
, _("Callers"), PROFILE_CALLER_NAME
);
1389 add_sample_column (tree_view
, _("Self"), PROFILE_CALLER_SELF
);
1390 add_sample_column (tree_view
, _("Total"), PROFILE_CALLER_TOTAL
);
1391 gtk_tree_view_column_set_expand (col
, TRUE
);
1393 g_signal_connect (tree_view
, "row-activated",
1394 G_CALLBACK (profile_caller_row_activated
), pwin
);
1396 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1400 setup_leak_block_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1402 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (tree_view
);
1403 GtkListStore
*store
;
1405 g_return_if_fail (selection
!= NULL
);
1407 store
= gtk_list_store_new (4,
1413 gtk_tree_view_set_model (tree_view
, GTK_TREE_MODEL (store
));
1415 add_pointer_column (tree_view
, _("Address"), LEAK_BLOCK_ADDR
);
1416 add_plain_text_column (tree_view
, _("Size"), LEAK_BLOCK_SIZE
);
1417 add_plain_text_column (tree_view
, _("Caller"), LEAK_BLOCK_CALLER
);
1419 g_signal_connect (selection
, "changed",
1420 G_CALLBACK (leak_block_selection_changed
), pwin
);
1422 gtk_tree_view_columns_autosize (tree_view
);
1426 setup_leak_stack_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1428 GtkListStore
*store
;
1430 store
= gtk_list_store_new (3,
1435 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view
), GTK_TREE_MODEL (store
));
1437 add_plain_text_column (tree_view
, _("Function"), LEAK_STACK_NAME
);
1438 add_plain_text_column (tree_view
, _("Line"), LEAK_STACK_LINE
);
1439 add_plain_text_column (tree_view
, _("File"), LEAK_STACK_FILE
);
1441 g_signal_connect (tree_view
, "row-activated",
1442 G_CALLBACK (leak_stack_row_activated
), pwin
);
1444 gtk_tree_view_columns_autosize (tree_view
);
1448 set_default_size (GtkWindow
*window
)
1452 GdkRectangle monitor
;
1454 GtkWidget
*widget
= GTK_WIDGET (window
);
1456 screen
= gtk_widget_get_screen (widget
);
1457 monitor_num
= gdk_screen_get_monitor_at_window (screen
, widget
->window
);
1459 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
1461 width
= monitor
.width
* 3 / 4;
1462 height
= monitor
.height
* 3 / 4;
1464 gtk_window_resize (window
, width
, height
);
1467 static ProcessWindow
*
1468 process_window_new (void)
1470 gchar
*fullfilename
;
1474 ProcessWindow
*pwin
;
1477 pwin
= g_new0 (ProcessWindow
, 1);
1478 process_windows
= g_slist_prepend (process_windows
, pwin
);
1480 pwin
->process
= NULL
;
1481 pwin
->profile
= NULL
;
1484 pwin
->usage_max
= 32*1024;
1485 pwin
->usage_high
= 0;
1486 pwin
->usage_leaked
= 0;
1488 xml
= glade_xml_new (glade_file
, "MainWindow", NULL
);
1490 pwin
->main_window
= get_widget (xml
, "MainWindow");
1491 gtk_widget_realize (pwin
->main_window
);
1493 fullfilename
= g_strdup ("./memprof.png");
1495 if (!g_file_test (fullfilename
, G_FILE_TEST_EXISTS
))
1496 fullfilename
= g_build_filename (DATADIR
, "memprof.png", NULL
);
1499 gtk_window_set_icon_from_file (GTK_WINDOW (pwin
->main_window
), fullfilename
, &err
);
1501 g_free (fullfilename
);
1503 g_signal_connect (pwin
->main_window
, "delete_event",
1504 G_CALLBACK (hide_and_check_quit
), pwin
);
1507 set_default_size (GTK_WINDOW (pwin
->main_window
));
1509 g_object_set_data_full (G_OBJECT (pwin
->main_window
),
1511 pwin
, (GDestroyNotify
)process_window_free
);
1513 pwin
->main_notebook
= get_widget (xml
, "main-notebook");
1515 pwin
->n_allocations_label
= get_widget (xml
, "n-allocations-label");
1516 pwin
->bytes_per_label
= get_widget (xml
, "bytes-per-label");
1517 pwin
->total_bytes_label
= get_widget (xml
, "total-bytes-label");
1519 pwin
->profile_status_label
= get_widget (xml
, "profile-status-label");
1521 /* setup profile tree views */
1522 pwin
->profile_func_tree_view
= get_widget (xml
, "profile-func-tree-view");
1523 pwin
->profile_descendants_tree_view
=
1524 get_widget (xml
, "profile-descendants-tree-view");
1525 pwin
->profile_caller_tree_view
= get_widget (xml
, "profile-caller-tree-view");
1527 setup_profile_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
1528 setup_profile_descendants_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
));
1529 setup_profile_caller_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
1531 /* leak tree views */
1532 pwin
->leak_block_tree_view
= get_widget (xml
, "leak-block-tree-view");
1533 pwin
->leak_stack_tree_view
= get_widget (xml
, "leak-stack-tree-view");
1535 setup_leak_block_tree_view (pwin
, GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
1536 setup_leak_stack_tree_view (pwin
, GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
1538 pwin
->usage_max_label
= get_widget (xml
, "usage-max-label");
1539 pwin
->usage_area
= get_widget (xml
, "usage-area");
1541 g_signal_connect (pwin
->usage_area
, "expose_event",
1542 G_CALLBACK (on_usage_area_expose
), pwin
);
1544 vpaned
= get_widget (xml
, "profile-vpaned");
1545 gtk_paned_set_position (GTK_PANED (vpaned
), 150);
1547 hpaned
= get_widget (xml
, "profile-hpaned");
1548 gtk_paned_set_position (GTK_PANED (hpaned
), 150);
1550 vpaned
= get_widget (xml
, "leaks-vpaned");
1551 gtk_paned_set_position (GTK_PANED (vpaned
), 150);
1553 /* If profiling time, not memory, hide all GUI related to leak
1556 if (profile_type
!= MP_PROFILE_MEMORY
) {
1557 gtk_widget_hide (get_widget (xml
, "leaks-vpaned"));
1558 gtk_widget_hide (get_widget (xml
, "toolbar-leaks-button"));
1559 gtk_widget_hide (get_widget (xml
, "save-leak-info"));
1560 gtk_widget_hide (get_widget (xml
, "generate-leak-report"));
1561 gtk_widget_hide (get_widget (xml
, "allocation-bar"));
1562 gtk_notebook_set_show_tabs (
1563 GTK_NOTEBOOK (get_widget (xml
, "main-notebook")), FALSE
);
1566 gtk_widget_hide (get_widget (xml
, "profile-status-label"));
1567 gtk_widget_hide (get_widget (xml
, "reset-profile-button"));
1570 pwin
->time_graph
= get_widget(xml
, "time-graph");
1571 pwin
->mem_map
= get_widget(xml
, "mem-map");
1572 g_signal_connect(pwin
->time_graph
, "expose_event",
1573 G_CALLBACK (time_graph_expose_event
), pwin
);
1574 g_signal_connect(pwin
->mem_map
, "expose_event",
1575 G_CALLBACK (mem_map_expose_event
), pwin
);
1577 glade_xml_signal_autoconnect (xml
);
1578 g_object_unref (G_OBJECT (xml
));
1585 process_window_get_process (ProcessWindow
*pwin
)
1587 return pwin
->process
;
1591 process_window_visible (ProcessWindow
*pwin
)
1593 return GTK_WIDGET_VISIBLE (pwin
->main_window
);
1597 process_window_show (ProcessWindow
*pwin
)
1599 if (!process_window_visible (pwin
))
1600 gtk_widget_show (pwin
->main_window
);
1602 gdk_window_show (pwin
->main_window
->window
);
1606 process_window_hide (ProcessWindow
*pwin
)
1608 if (process_window_visible (pwin
))
1609 hide_and_check_quit (pwin
->main_window
);
1615 GList
*toplevels
, *tmplist
;
1617 tmplist
= toplevels
= gtk_window_list_toplevels ();
1619 if (GTK_WIDGET_VISIBLE (toplevels
->data
))
1621 tmplist
= tmplist
->next
;
1624 g_list_free (toplevels
);
1630 hide_and_check_quit (GtkWidget
*window
)
1632 gtk_widget_hide (window
);
1640 process_window_maybe_detach (ProcessWindow
*pwin
)
1643 const char *message
;
1646 if (pwin
->process
->status
== MP_PROCESS_EXITING
)
1647 message
= _("Really detach from finished process?");
1649 message
= _("Really detach from running process?");
1651 dialog
= gtk_message_dialog_new (GTK_WINDOW (pwin
->main_window
),
1652 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1653 GTK_MESSAGE_QUESTION
,
1656 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_YES
);
1658 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1659 gtk_widget_destroy (dialog
);
1661 if (response
== GTK_RESPONSE_YES
)
1662 process_detach (pwin
->process
);
1667 process_window_maybe_kill (ProcessWindow
*pwin
)
1669 if (pwin
->process
->status
== MP_PROCESS_EXITING
)
1670 process_window_maybe_detach (pwin
);
1675 dialog
= gtk_message_dialog_new (GTK_WINDOW (pwin
->main_window
),
1676 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1677 GTK_MESSAGE_QUESTION
,
1679 _("Really kill running process?"));
1680 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
1683 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1684 gtk_widget_destroy (dialog
);
1686 if (response
== GTK_RESPONSE_YES
)
1687 process_kill (pwin
->process
);
1692 sigchld_handler (int signum
)
1694 int old_errno
= errno
;
1697 int pid
= waitpid (WAIT_ANY
, NULL
, WNOHANG
);
1698 if (pid
< 0 && errno
!= ECHILD
)
1699 g_error ("waitpid: %s\n", g_strerror (errno
));
1707 static char *profile_type_string
= NULL
;
1708 static char *profile_rate_string
= NULL
;
1709 static int profile_interval
= 1000;
1710 static gchar
**profile_skip_funcs
= NULL
;
1712 static const GOptionEntry entries
[] =
1714 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE
, &default_follow_fork
,
1715 N_("Create new windows for forked processes"), NULL
},
1716 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE
, &default_follow_exec
,
1717 N_("Retain windows for processes after exec()"), NULL
},
1718 { "profile", '\0', 0, G_OPTION_ARG_STRING
, &profile_type_string
,
1719 N_("Type of profiling information to collect"), "memory/cycles/time" },
1720 { "rate", '\0', 0, G_OPTION_ARG_STRING
, &profile_rate_string
,
1721 N_("Number of samples/sec for time profile (1k=1000)"), NULL
},
1722 { "skip-funcs", '\0', 0, G_OPTION_ARG_STRING_ARRAY
, &profile_skip_funcs
,
1723 N_("Functions allocating memory"), "function_name" },
1728 parse_options (int *argc
, char ***argv
)
1731 GOptionContext
*context
;
1733 context
= g_option_context_new ("- A memory profiler");
1735 g_option_context_add_main_entries (context
, entries
, GETTEXT_PACKAGE
);
1736 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
1737 g_option_context_parse (context
, argc
, argv
, &err
);
1741 initialize_skip_funcs ()
1745 /* C library functions */
1746 skip_funcs
= g_slist_append (skip_funcs
, "malloc");
1747 skip_funcs
= g_slist_append (skip_funcs
, "calloc");
1748 skip_funcs
= g_slist_append (skip_funcs
, "realloc");
1749 skip_funcs
= g_slist_append (skip_funcs
, "strdup");
1750 skip_funcs
= g_slist_append (skip_funcs
, "strndup");
1753 skip_funcs
= g_slist_append (skip_funcs
, "g_malloc");
1754 skip_funcs
= g_slist_append (skip_funcs
, "g_malloc0");
1755 skip_funcs
= g_slist_append (skip_funcs
, "g_realloc");
1756 skip_funcs
= g_slist_append (skip_funcs
, "g_strdup");
1757 skip_funcs
= g_slist_append (skip_funcs
, "g_strndup");
1758 skip_funcs
= g_slist_append (skip_funcs
, "g_slice_alloc");
1759 skip_funcs
= g_slist_append (skip_funcs
, "g_slice_alloc0");
1760 skip_funcs
= g_slist_append (skip_funcs
, "g_memdup");
1763 skip_funcs
= g_slist_append (skip_funcs
, "_Znwj");
1766 skip_funcs
= g_slist_append (skip_funcs
, "_ZN3WTF16fastZeroedMallocEj");
1767 skip_funcs
= g_slist_append (skip_funcs
, "_ZN3WTF10fastStrDupEPKc");
1768 skip_funcs
= g_slist_append (skip_funcs
, "_ZN3WTF10fastCallocEjj");
1769 skip_funcs
= g_slist_append (skip_funcs
, "_ZN3WTF10fastMallocEj");
1770 skip_funcs
= g_slist_append (skip_funcs
, "_ZN3WTF11fastReallocEPvj");
1773 skip_funcs
= g_slist_append (skip_funcs
, "_Z7qMallocj");
1774 skip_funcs
= g_slist_append (skip_funcs
, "_Z8qReallocPvj");
1777 skip_funcs
= g_slist_append (skip_funcs
, "_hb_alloc");
1779 while (profile_skip_funcs
&& profile_skip_funcs
[i
]) {
1780 skip_funcs
= g_slist_append (skip_funcs
, profile_skip_funcs
[i
]);
1786 main(int argc
, char **argv
)
1788 ProcessWindow
*initial_window
;
1790 gtk_init (&argc
, &argv
);
1792 parse_options (&argc
, &argv
);
1794 /* Set up a handler for SIGCHLD to avoid zombie children
1796 signal (SIGCHLD
, sigchld_handler
);
1798 bindtextdomain (GETTEXT_PACKAGE
, LOCALEDIR
);
1799 textdomain (GETTEXT_PACKAGE
);
1801 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1802 bind_textdomain_codeset (GETTEXT_PACKAGE
, "UTF-8");
1805 /* If the user didn't specify the profile type explicitely,
1806 * we guess from the executable name.
1808 if (!profile_type_string
) {
1810 basename
= g_path_get_basename (argv
[0]);
1812 if (strcmp (basename
, "speedprof") == 0)
1813 profile_type_string
= "cycles";
1815 profile_type_string
= "memory";
1820 if (strcmp (profile_type_string
, "memory") == 0)
1821 profile_type
= MP_PROFILE_MEMORY
;
1822 else if (strcmp (profile_type_string
, "cycles") == 0)
1823 profile_type
= MP_PROFILE_CYCLES
;
1824 else if (strcmp (profile_type_string
, "time") == 0)
1825 profile_type
= MP_PROFILE_TIME
;
1827 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1831 initialize_skip_funcs();
1833 if (profile_rate_string
) {
1836 int len
= strlen (profile_rate_string
);
1837 char suffix
[2] = { '\0', '\0' };
1841 (profile_rate_string
[len
- 1] == 'k' ||
1842 profile_rate_string
[len
- 1] == 'K')) {
1843 suffix
[0] = profile_rate_string
[len
- 1];
1845 profile_rate_string
[len
- 1] = '\0';
1848 rate
= strtod (profile_rate_string
, &end
);
1849 if (len
== 0 || *end
!= '\0' ||
1850 rate
* multiplier
<= 1 || rate
* multiplier
> 1000000) {
1851 g_printerr ("Invalid rate: %s%s\n",
1852 profile_rate_string
, suffix
);
1856 profile_interval
= (int) (0.5 + 1000000 / (rate
* multiplier
));
1859 glade_file
= "./memprof.glade";
1860 if (!g_file_test (glade_file
, G_FILE_TEST_EXISTS
)) {
1861 glade_file
= g_build_filename (DATADIR
, "memprof.glade", NULL
);
1863 if (!g_file_test (glade_file
, G_FILE_TEST_EXISTS
)) {
1864 show_error (NULL
, ERROR_FATAL
, _("Cannot find memprof.glade"));
1867 global_server
= mp_server_new ();
1868 mp_server_set_profile_type (global_server
, profile_type
);
1869 mp_server_set_interval (global_server
, profile_interval
);
1871 g_signal_connect (global_server
, "process_created",
1872 G_CALLBACK (process_created_cb
), NULL
);
1874 initial_window
= process_window_new ();
1876 gtk_widget_show (initial_window
->main_window
);
1881 if (strcmp (argv
[start
], "--") == 0)
1884 run_file (initial_window
, (char **)(argv
+ start
));