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, 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
;
393 int n_samples
= pwin
->profile
->n_bytes
;
395 GtkSortType old_sort_type
;
398 if (!gtk_tree_selection_get_selected (selection
, (GtkTreeModel
**)&store
, &selected
))
400 g_warning ("No selection");
404 gtk_tree_model_get (GTK_TREE_MODEL (store
), &selected
,
405 PROFILE_FUNC_FUNC
, &func
,
408 was_sorted
= get_sort_info (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
),
409 &old_sort_column
, &old_sort_type
);
411 /* fill descendants tree */
412 tree_store
= gtk_tree_store_new (4,
418 descendant_tree
= profile_func_create_descendant_tree (func
);
420 add_node (tree_store
, n_samples
, NULL
, descendant_tree
->roots
->pdata
[0]);
422 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), TRUE
);
424 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
),
425 GTK_TREE_MODEL (tree_store
));
427 /* Expand the toplevel of the descendant tree so we see the immediate
430 path
= gtk_tree_path_new_from_indices (0, -1);
431 gtk_tree_view_expand_row (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
), path
, FALSE
);
432 gtk_tree_path_free (path
);
435 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store
),
436 old_sort_column
, old_sort_type
);
438 gtk_tree_sortable_set_sort_column_id (
439 GTK_TREE_SORTABLE (tree_store
), 2, GTK_SORT_DESCENDING
);
441 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (tree_store
));
443 g_object_unref (G_OBJECT (tree_store
));
445 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
));
447 profile_descendant_tree_free (descendant_tree
);
449 /* fill caller tree */
450 was_sorted
= get_sort_info (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
),
451 &old_sort_column
, &old_sort_type
);
453 list_store
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_DOUBLE
, G_TYPE_DOUBLE
, G_TYPE_POINTER
);
454 list_model
= GTK_TREE_MODEL (list_store
);
456 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
458 caller_list
= profile_func_create_caller_list (func
);
460 for (i
= 0; i
< caller_list
->len
; ++i
) {
463 ProfileFunc
*caller
= caller_list
->pdata
[i
];
466 if (caller
->node
->symbol
)
467 name
= elf_demangle(caller
->node
->symbol
);
469 name
= g_strdup("???");
472 name
= g_strdup("<spontaneous>");
474 gtk_list_store_append (list_store
, &iter
);
476 gtk_list_store_set (list_store
, &iter
,
477 PROFILE_CALLER_NAME
, name
,
478 PROFILE_CALLER_SYMBOL
, caller
->node
? caller
->node
->symbol
: GINT_TO_POINTER (-1),
481 set_sample (list_model
, &iter
, PROFILE_CALLER_SELF
, caller
->self
, n_samples
);
482 set_sample (list_model
, &iter
, PROFILE_CALLER_TOTAL
, caller
->total
, n_samples
);
486 profile_caller_list_free (caller_list
);
488 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
490 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
),
494 gtk_tree_sortable_set_sort_column_id (
495 GTK_TREE_SORTABLE (list_store
), old_sort_column
, old_sort_type
);
497 gtk_tree_sortable_set_sort_column_id (
498 GTK_TREE_SORTABLE (list_store
), 2, GTK_SORT_DESCENDING
);
500 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (list_store
));
502 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
504 g_object_unref (G_OBJECT (list_store
));
506 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_caller_tree_view
), TRUE
);
510 profile_fill (ProcessWindow
*pwin
)
516 int n_samples
= pwin
->profile
->n_bytes
;
519 GtkSortType old_sort_type
;
523 get_sort_info (GTK_TREE_VIEW (pwin
->profile_func_tree_view
),
524 &old_sort_column
, &old_sort_type
);
526 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), NULL
);
527 store
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_DOUBLE
, G_TYPE_DOUBLE
, G_TYPE_POINTER
);
528 model
= GTK_TREE_MODEL (store
);
530 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), model
);
532 /* inserting in a ListStore is O(n) when sorting ... */
533 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
535 for (i
= 0; i
< pwin
->profile
->functions
->len
; ++i
) {
539 ProfileFunc
*func
= pwin
->profile
->functions
->pdata
[i
];
541 gtk_list_store_append (store
, &iter
);
545 if (func
->node
->symbol
)
546 name
= elf_demangle(func
->node
->symbol
);
548 name
= g_strdup("???");
550 gtk_list_store_set (store
, &iter
,
551 PROFILE_FUNC_NAME
, name
,
552 PROFILE_FUNC_FUNC
, func
,
555 set_sample (model
, &iter
, PROFILE_FUNC_SELF
, func
->self
, n_samples
);
556 set_sample (model
, &iter
, PROFILE_FUNC_TOTAL
, func
->total
, n_samples
);
561 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
564 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store
), old_sort_column
,
568 gtk_tree_sortable_set_sort_column_id (
569 GTK_TREE_SORTABLE (store
), 2, GTK_SORT_DESCENDING
);
572 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (store
));
574 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_func_tree_view
), TRUE
);
576 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
578 g_object_unref (G_OBJECT (store
));
582 /************************************************************
583 * GUI for leak detection
584 ************************************************************/
587 leak_block_get_selected (ProcessWindow
*pwin
)
589 GtkTreeSelection
*selection
;
594 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
595 if (selection
&& gtk_tree_selection_get_selected (selection
, &model
, &iter
))
596 gtk_tree_model_get (model
, &iter
, LEAK_BLOCK_BLOCK
, &block
, -1);
602 leak_block_selection_changed (GtkTreeSelection
*selection
,
605 GtkTreeModel
*model
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
606 GtkListStore
*store
= GTK_LIST_STORE (model
);
607 Block
*block
= leak_block_get_selected (pwin
);
610 gtk_list_store_clear (store
);
615 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
617 for (stack
= block
->stack
; stack
!= NULL
; stack
= stack
->parent
) {
619 const char *filename
;
623 if (!process_find_line (pwin
->process
, stack
->address
,
624 &filename
, &functionname
, &line
)) {
625 /* 0x3f == '?' -- suppress trigraph warnings */
626 functionname
= "(\x3f\x3f\x3f)";
627 filename
= "(\x3f\x3f\x3f)";
631 gtk_list_store_append (store
, &iter
);
632 gtk_list_store_set (store
, &iter
,
633 LEAK_STACK_NAME
, functionname
,
634 LEAK_STACK_LINE
, line
,
635 LEAK_STACK_FILE
, filename
,
639 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
643 leaks_fill (ProcessWindow
*pwin
)
649 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
650 store
= GTK_LIST_STORE (model
);
652 gtk_list_store_clear (store
);
654 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
656 tmp_list
= pwin
->leaks
;
658 const char *filename
;
659 gboolean free_function
= FALSE
;
660 char *functionname
= NULL
;
665 Block
*block
= tmp_list
->data
;
668 for (stack
= block
->stack
; stack
!= NULL
; stack
= stack
->parent
) {
669 if (process_find_line (pwin
->process
, stack
->address
,
670 &filename
, &functionname
, &line
)) {
676 for (tmp_list
= skip_funcs
; tmp_list
!= NULL
; tmp_list
= tmp_list
->next
) {
677 if (!strcmp (functionname
, tmp_list
->data
)) {
686 for (tmp_list
= skip_regexes
; tmp_list
!= NULL
; tmp_list
= tmp_list
->next
) {
689 regcomp (®ex
, tmp_list
->data
, 0);
691 if (!regexec (®ex
, functionname
, 0, NULL
, 0)) {
705 free_function
= TRUE
;
706 functionname
= g_strdup ("(\x3f\x3f\x3f)");
709 gtk_list_store_append (store
, &iter
);
710 gtk_list_store_set (store
, &iter
,
711 LEAK_BLOCK_ADDR
, block
->addr
,
712 LEAK_BLOCK_SIZE
, block
->size
,
713 LEAK_BLOCK_CALLER
, functionname
,
714 LEAK_BLOCK_BLOCK
, block
,
718 g_free (functionname
);
720 tmp_list
= tmp_list
->next
;
723 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
727 leak_stack_run_command (ProcessWindow
*pwin
, Block
*block
, int frame
)
729 const char *filename
;
733 StackNode
*stack
= block
->stack
;
735 stack
= stack
->parent
;
737 if (process_find_line (pwin
->process
, stack
->address
,
738 &filename
, &functionname
, &line
)) {
740 GString
*command
= g_string_new (NULL
);
741 char *p
= stack_command
;
750 g_string_append (command
, filename
);
753 snprintf(buf
, 32, "%d", line
);
754 g_string_append (command
, buf
);
757 g_string_append_c (command
, '%');
760 g_string_append_c (command
, '%');
761 g_string_append_c (command
, *p
);
764 g_string_append_c (command
, *p
);
768 cmdline
= g_strdup_printf ("/bin/sh -c %s", command
->str
);
770 if (!g_spawn_command_line_async (cmdline
, &err
)) {
771 show_error (pwin
->main_window
,
772 ERROR_MODAL
, _("Execution of \"%s\" failed: %s"),
773 command
->str
, err
->message
);
780 g_string_free (command
, FALSE
);
785 leak_stack_row_activated (GtkTreeView
*tree_view
,
795 model
= gtk_tree_view_get_model (tree_view
);
796 gtk_tree_model_get_iter (model
, &iter
, path
);
797 frame
= list_iter_get_index (model
, &iter
);
799 block
= leak_block_get_selected (pwin
);
801 leak_stack_run_command (pwin
, block
, frame
);
806 /************************************************************
807 * File Selection handling
808 ************************************************************/
811 get_filename (const gchar
*title
,
812 const gchar
*suggested_name
)
815 gchar
*filename
= NULL
;
816 dialog
= gtk_file_chooser_dialog_new (title
,
817 NULL
, GTK_FILE_CHOOSER_ACTION_SAVE
,
818 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
819 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
821 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog
), suggested_name
);
822 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
823 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
825 gtk_widget_destroy (dialog
);
829 /* Really ugly utility function to retrieve the ProcessWindow from
830 * either a menu_item or toolbar item.
833 pwin_from_widget (GtkWidget
*widget
)
837 if (GTK_IS_MENU_ITEM (widget
)) {
838 GtkWidget
*menu_shell
= widget
->parent
;
840 while (menu_shell
&& !GTK_IS_MENU_BAR (menu_shell
)) {
841 menu_shell
= gtk_menu_get_attach_widget (GTK_MENU (menu_shell
))->parent
;
843 g_assert (menu_shell
!= NULL
);
845 app
= gtk_widget_get_toplevel (menu_shell
);
847 app
= gtk_widget_get_toplevel (widget
);
849 return g_object_get_data (G_OBJECT (app
), "process-window");
853 close_cb (GtkWidget
*widget
)
855 ProcessWindow
*pwin
= pwin_from_widget (widget
);
857 hide_and_check_quit (pwin
->main_window
);
861 exit_cb (GtkWidget
*widget
)
867 reset_cb (MPProcess
*process
, ProcessWindow
*pwin
)
869 process_window_reset (pwin
);
873 status_changed_cb (MPProcess
*process
, ProcessWindow
*pwin
)
875 if (process
->status
== MP_PROCESS_DEFUNCT
||
876 process
->status
== MP_PROCESS_DETACHED
) {
878 if (g_slist_length (process_windows
) > 1) {
879 tree_window_remove (pwin
);
880 process_window_destroy (pwin
);
882 tree_window_remove (pwin
);
885 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin
->process
), G_CALLBACK (status_changed_cb
), pwin
);
886 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin
->process
), G_CALLBACK (reset_cb
), pwin
);
887 g_object_unref (G_OBJECT (pwin
->process
));
888 pwin
->process
= NULL
;
891 if (pwin
->status_update_timeout
) {
892 g_source_remove (pwin
->status_update_timeout
);
893 pwin
->status_update_timeout
= 0;
896 process_window_reset (pwin
);
900 char *status
= process_get_status_text (process
);
901 char *cmdline
= process_get_cmdline (process
);
902 char *title
= g_strdup_printf ("%s - %s (%d) - %s", _("MemProf"), cmdline
, process
->pid
, status
);
903 gtk_window_set_title (GTK_WINDOW (pwin
->main_window
), title
);
912 list_view_clear (GtkTreeView
*tree_view
)
914 GtkTreeModel
*model
= gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view
));
915 gtk_list_store_clear (GTK_LIST_STORE (model
));
919 process_window_reset (ProcessWindow
*pwin
)
922 profile_free (pwin
->profile
);
923 pwin
->profile
= NULL
;
925 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), NULL
);
926 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
), NULL
);
927 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
), NULL
);
929 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_func_tree_view
), FALSE
);
930 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_caller_tree_view
), FALSE
);
931 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), FALSE
);
935 g_slist_free (pwin
->leaks
);
937 list_view_clear (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
938 list_view_clear (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
941 pwin
->usage_max
= 32*1024;
942 pwin
->usage_high
= 0;
943 pwin
->usage_leaked
= 0;
945 gtk_window_set_title (GTK_WINDOW (pwin
->main_window
), "MemProf");
947 gtk_widget_queue_draw (pwin
->usage_area
);
952 init_process (ProcessWindow
*pwin
, MPProcess
*process
)
954 pwin
->process
= process
;
956 process_set_follow_fork (pwin
->process
, default_follow_fork
);
957 process_set_follow_exec (pwin
->process
, default_follow_exec
);
959 pwin
->status_update_timeout
=
964 g_signal_connect (process
, "status_changed",
965 G_CALLBACK (status_changed_cb
), pwin
);
966 g_signal_connect (process
, "reset",
967 G_CALLBACK (reset_cb
), pwin
);
969 tree_window_add (pwin
);
973 process_created_cb (MPServer
*server
, MPProcess
*process
)
975 ProcessWindow
*pwin
= process_window_new ();
977 init_process (pwin
, process
);
983 run_file (ProcessWindow
*pwin
, char **args
)
988 g_return_val_if_fail (args
!= NULL
, FALSE
);
989 g_return_val_if_fail (args
[0] != NULL
, FALSE
);
991 path
= process_find_exec (args
);
994 g_warning ("Process new '%s'\n", path
);
995 MPProcess
*process
= process_new (global_server
);
996 process_run (process
, path
, args
);
999 pwin
= process_window_new ();
1000 tree_window_show ();
1003 init_process (pwin
, process
);
1005 gtk_widget_show (pwin
->main_window
);
1010 show_error (pwin
->main_window
,
1012 _("Cannot find executable for \"%s\""),
1023 run_cb (GtkWidget
*widget
)
1026 GtkWidget
*run_dialog
;
1027 GtkWidget
*filechooser
;
1029 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1031 xml
= glade_xml_new (glade_file
, "RunDialog", NULL
);
1032 run_dialog
= get_widget (xml
, "RunDialog");
1033 filechooser
= get_widget (xml
, "RunDialog-chooser");
1035 g_object_unref (G_OBJECT (xml
));
1038 gtk_window_set_transient_for (GTK_WINDOW (run_dialog
),
1039 GTK_WINDOW (pwin
->main_window
));
1040 if (gtk_dialog_run (GTK_DIALOG (run_dialog
)) == 1) {
1045 text
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser
));
1049 args
= process_parse_exec (text
);
1051 result
= run_file (pwin
, args
);
1063 gtk_widget_destroy (run_dialog
);
1067 kill_cb (GtkWidget
*widget
)
1069 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1072 process_window_maybe_kill (pwin
);
1076 detach_cb (GtkWidget
*widget
)
1078 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1081 process_window_maybe_detach (pwin
);
1085 process_tree_cb (GtkWidget
*widget
)
1087 tree_window_show ();
1091 save_leak_cb (GtkWidget
*widget
)
1093 static gchar
*suggestion
= NULL
;
1096 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1099 filename
= get_filename ("Save Leak Report",
1100 suggestion
? suggestion
: "memprof.leak");
1102 g_free (suggestion
);
1103 suggestion
= filename
;
1105 leaks_print (pwin
->process
, pwin
->leaks
, filename
);
1112 save_profile_cb (GtkWidget
*widget
)
1114 static gchar
*suggestion
= NULL
;
1117 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1119 if (pwin
->profile
) {
1120 filename
= get_filename ("Save Profile",
1121 suggestion
? suggestion
: "memprof.out");
1123 g_free (suggestion
);
1124 suggestion
= filename
;
1126 profile_write (pwin
->profile
, filename
);
1133 save_current_cb (GtkWidget
*widget
)
1135 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1137 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin
->main_notebook
))) {
1139 save_profile_cb (widget
);
1142 save_leak_cb (widget
);
1148 generate_leak_cb (GtkWidget
*widget
)
1151 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1154 if (pwin
->process
) {
1155 process_stop_input (pwin
->process
);
1157 while (pwin
->leaks
) {
1158 block_unref(pwin
->leaks
->data
);
1159 pwin
->leaks
= g_slist_delete_link(pwin
->leaks
, pwin
->leaks
);
1161 pwin
->leaks
= leaks_find (pwin
->process
);
1163 pwin
->usage_leaked
= 0;
1164 tmp_list
= pwin
->leaks
;
1166 pwin
->usage_leaked
+= ((Block
*)tmp_list
->data
)->size
;
1167 tmp_list
= tmp_list
->next
;
1171 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin
->main_notebook
), 1);
1173 process_start_input (pwin
->process
);
1178 generate_profile_cb (GtkWidget
*widget
)
1180 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1182 if (pwin
->process
) {
1183 process_stop_input (pwin
->process
);
1185 if (pwin
->profile
) {
1186 profile_free (pwin
->profile
);
1187 pwin
->profile
= NULL
;
1190 pwin
->profile
= profile_create (pwin
->process
, skip_funcs
);
1191 process_start_input (pwin
->process
);
1192 profile_fill (pwin
);
1194 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin
->main_notebook
), 0);
1199 reset_profile_cb (GtkWidget
*widget
)
1201 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1203 process_window_reset (pwin
);
1206 process_clear_input (pwin
->process
);
1210 record_button_toggled_cb (GtkWidget
*widget
)
1212 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1214 if (gtk_toggle_tool_button_get_active
1215 (GTK_TOGGLE_TOOL_BUTTON (widget
)))
1216 process_start_input (pwin
->process
);
1218 process_stop_input (pwin
->process
);
1222 about_cb (GtkWidget
*widget
)
1224 #define OSLASH "\303\270"
1225 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1227 /* FIXME: restore credits */
1228 gtk_show_about_dialog (GTK_WINDOW (pwin
->main_window
),
1234 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1235 "Copyright 2002, Kristian Rietveld\n"
1236 "Copyright 2002, 2006, 2007, S"OSLASH
"ren Sandmann",
1238 "copyright", "Copyright 2004-2006, S"OSLASH
"ren Sandmann",
1240 "version", PACKAGE_VERSION
,
1245 show_error_response (GtkDialog
*dialog
,
1249 if (response_id
== GTK_RESPONSE_OK
)
1250 gtk_widget_destroy (GTK_WIDGET (dialog
));
1254 show_error (GtkWidget
*parent_window
,
1256 const gchar
*format
,
1263 va_start (args
, format
);
1264 g_vasprintf (&message
, format
, args
);
1267 dialog
= gtk_message_dialog_new (parent_window
? GTK_WINDOW (parent_window
) : NULL
,
1268 GTK_DIALOG_DESTROY_WITH_PARENT
,
1269 (error
== ERROR_FATAL
) ?
1271 GTK_MESSAGE_WARNING
,
1272 GTK_BUTTONS_OK
, "%s", message
);
1275 gtk_window_set_title (GTK_WINDOW (dialog
),
1276 (error
== ERROR_FATAL
) ?
1277 _("MemProf Error") : _("MemProf Warning"));
1279 if (error
== ERROR_WARNING
) {
1280 gtk_widget_show (dialog
);
1281 g_signal_connect (dialog
, "response",
1282 G_CALLBACK (show_error_response
), NULL
);
1284 gtk_dialog_run (GTK_DIALOG (dialog
));
1285 gtk_widget_destroy (dialog
);
1288 if (error
== ERROR_FATAL
)
1293 process_window_free (ProcessWindow
*pwin
)
1295 /* FIXME: we leak the process structure */
1298 g_slist_free (pwin
->leaks
);
1301 profile_free (pwin
->profile
);
1303 process_windows
= g_slist_remove (process_windows
, pwin
);
1304 if (!process_windows
)
1312 process_window_destroy (ProcessWindow
*pwin
)
1314 if (pwin
->status_update_timeout
)
1315 g_source_remove (pwin
->status_update_timeout
);
1317 gtk_widget_destroy (pwin
->main_window
);
1321 static GtkTreeViewColumn
*
1322 add_sample_column (GtkTreeView
*view
, const gchar
*title
, gint model_column
)
1326 if (profile_type
== MP_PROFILE_MEMORY
)
1331 return add_double_format_column (view
, title
, model_column
, format
);
1335 setup_profile_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1337 GtkTreeSelection
*selection
;
1338 GtkTreeViewColumn
*col
;
1340 col
= add_plain_text_column (tree_view
, _("Functions"), PROFILE_FUNC_NAME
);
1341 add_sample_column (tree_view
, _("Self"), PROFILE_FUNC_SELF
);
1342 add_sample_column (tree_view
, _("Total"), PROFILE_FUNC_TOTAL
);
1343 gtk_tree_view_column_set_expand (col
, TRUE
);
1345 selection
= gtk_tree_view_get_selection (tree_view
);
1346 g_return_if_fail (selection
!= NULL
);
1347 g_signal_connect (selection
, "changed", G_CALLBACK (profile_selection_changed
), pwin
);
1349 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1353 setup_profile_descendants_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1355 GtkTreeViewColumn
*col
;
1357 col
= add_plain_text_column (tree_view
, _("Descendants"), PROFILE_DESCENDANTS_NAME
);
1358 add_sample_column (tree_view
, _("Self"), PROFILE_DESCENDANTS_SELF
);
1359 add_sample_column (tree_view
, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE
);
1360 gtk_tree_view_column_set_expand (col
, TRUE
);
1362 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), FALSE
);
1364 g_signal_connect (tree_view
, "row-activated",
1365 G_CALLBACK (profile_descendants_row_activated
), pwin
);
1367 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1371 setup_profile_caller_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1373 GtkTreeViewColumn
*col
;
1375 col
= add_plain_text_column (tree_view
, _("Callers"), PROFILE_CALLER_NAME
);
1376 add_sample_column (tree_view
, _("Self"), PROFILE_CALLER_SELF
);
1377 add_sample_column (tree_view
, _("Total"), PROFILE_CALLER_TOTAL
);
1378 gtk_tree_view_column_set_expand (col
, TRUE
);
1380 g_signal_connect (tree_view
, "row-activated",
1381 G_CALLBACK (profile_caller_row_activated
), pwin
);
1383 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1387 setup_leak_block_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1389 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (tree_view
);
1390 GtkListStore
*store
;
1392 g_return_if_fail (selection
!= NULL
);
1394 store
= gtk_list_store_new (4,
1400 gtk_tree_view_set_model (tree_view
, GTK_TREE_MODEL (store
));
1402 add_pointer_column (tree_view
, _("Address"), LEAK_BLOCK_ADDR
);
1403 add_plain_text_column (tree_view
, _("Size"), LEAK_BLOCK_SIZE
);
1404 add_plain_text_column (tree_view
, _("Caller"), LEAK_BLOCK_CALLER
);
1406 g_signal_connect (selection
, "changed",
1407 G_CALLBACK (leak_block_selection_changed
), pwin
);
1409 gtk_tree_view_columns_autosize (tree_view
);
1413 setup_leak_stack_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1415 GtkListStore
*store
;
1417 store
= gtk_list_store_new (3,
1422 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view
), GTK_TREE_MODEL (store
));
1424 add_plain_text_column (tree_view
, _("Function"), LEAK_STACK_NAME
);
1425 add_plain_text_column (tree_view
, _("Line"), LEAK_STACK_LINE
);
1426 add_plain_text_column (tree_view
, _("File"), LEAK_STACK_FILE
);
1428 g_signal_connect (tree_view
, "row-activated",
1429 G_CALLBACK (leak_stack_row_activated
), pwin
);
1431 gtk_tree_view_columns_autosize (tree_view
);
1435 set_default_size (GtkWindow
*window
)
1439 GdkRectangle monitor
;
1441 GtkWidget
*widget
= GTK_WIDGET (window
);
1443 screen
= gtk_widget_get_screen (widget
);
1444 monitor_num
= gdk_screen_get_monitor_at_window (screen
, widget
->window
);
1446 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
1448 width
= monitor
.width
* 3 / 4;
1449 height
= monitor
.height
* 3 / 4;
1451 gtk_window_resize (window
, width
, height
);
1454 static ProcessWindow
*
1455 process_window_new (void)
1457 gchar
*fullfilename
;
1461 ProcessWindow
*pwin
;
1464 pwin
= g_new0 (ProcessWindow
, 1);
1465 process_windows
= g_slist_prepend (process_windows
, pwin
);
1467 pwin
->process
= NULL
;
1468 pwin
->profile
= NULL
;
1471 pwin
->usage_max
= 32*1024;
1472 pwin
->usage_high
= 0;
1473 pwin
->usage_leaked
= 0;
1475 xml
= glade_xml_new (glade_file
, "MainWindow", NULL
);
1477 pwin
->main_window
= get_widget (xml
, "MainWindow");
1478 gtk_widget_realize (pwin
->main_window
);
1480 fullfilename
= g_strdup ("./memprof.png");
1482 if (!g_file_test (fullfilename
, G_FILE_TEST_EXISTS
))
1483 fullfilename
= g_build_filename (DATADIR
, "memprof.png", NULL
);
1486 gtk_window_set_icon_from_file (GTK_WINDOW (pwin
->main_window
), fullfilename
, &err
);
1488 g_free (fullfilename
);
1490 g_signal_connect (pwin
->main_window
, "delete_event",
1491 G_CALLBACK (hide_and_check_quit
), pwin
);
1494 set_default_size (GTK_WINDOW (pwin
->main_window
));
1496 g_object_set_data_full (G_OBJECT (pwin
->main_window
),
1498 pwin
, (GDestroyNotify
)process_window_free
);
1500 pwin
->main_notebook
= get_widget (xml
, "main-notebook");
1502 pwin
->n_allocations_label
= get_widget (xml
, "n-allocations-label");
1503 pwin
->bytes_per_label
= get_widget (xml
, "bytes-per-label");
1504 pwin
->total_bytes_label
= get_widget (xml
, "total-bytes-label");
1506 pwin
->profile_status_label
= get_widget (xml
, "profile-status-label");
1508 /* setup profile tree views */
1509 pwin
->profile_func_tree_view
= get_widget (xml
, "profile-func-tree-view");
1510 pwin
->profile_descendants_tree_view
=
1511 get_widget (xml
, "profile-descendants-tree-view");
1512 pwin
->profile_caller_tree_view
= get_widget (xml
, "profile-caller-tree-view");
1514 setup_profile_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
1515 setup_profile_descendants_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
));
1516 setup_profile_caller_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
1518 /* leak tree views */
1519 pwin
->leak_block_tree_view
= get_widget (xml
, "leak-block-tree-view");
1520 pwin
->leak_stack_tree_view
= get_widget (xml
, "leak-stack-tree-view");
1522 setup_leak_block_tree_view (pwin
, GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
1523 setup_leak_stack_tree_view (pwin
, GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
1525 pwin
->usage_max_label
= get_widget (xml
, "usage-max-label");
1526 pwin
->usage_area
= get_widget (xml
, "usage-area");
1528 g_signal_connect (pwin
->usage_area
, "expose_event",
1529 G_CALLBACK (on_usage_area_expose
), pwin
);
1531 vpaned
= get_widget (xml
, "profile-vpaned");
1532 gtk_paned_set_position (GTK_PANED (vpaned
), 150);
1534 hpaned
= get_widget (xml
, "profile-hpaned");
1535 gtk_paned_set_position (GTK_PANED (hpaned
), 150);
1537 vpaned
= get_widget (xml
, "leaks-vpaned");
1538 gtk_paned_set_position (GTK_PANED (vpaned
), 150);
1540 /* If profiling time, not memory, hide all GUI related to leak
1543 if (profile_type
!= MP_PROFILE_MEMORY
) {
1544 gtk_widget_hide (get_widget (xml
, "leaks-vpaned"));
1545 gtk_widget_hide (get_widget (xml
, "toolbar-leaks-button"));
1546 gtk_widget_hide (get_widget (xml
, "save-leak-info"));
1547 gtk_widget_hide (get_widget (xml
, "generate-leak-report"));
1548 gtk_widget_hide (get_widget (xml
, "allocation-bar"));
1549 gtk_notebook_set_show_tabs (
1550 GTK_NOTEBOOK (get_widget (xml
, "main-notebook")), FALSE
);
1553 gtk_widget_hide (get_widget (xml
, "profile-status-label"));
1554 gtk_widget_hide (get_widget (xml
, "reset-profile-button"));
1557 pwin
->time_graph
= get_widget(xml
, "time-graph");
1558 pwin
->mem_map
= get_widget(xml
, "mem-map");
1559 g_signal_connect(pwin
->time_graph
, "expose_event",
1560 G_CALLBACK (time_graph_expose_event
), pwin
);
1561 g_signal_connect(pwin
->mem_map
, "expose_event",
1562 G_CALLBACK (mem_map_expose_event
), pwin
);
1564 glade_xml_signal_autoconnect (xml
);
1565 g_object_unref (G_OBJECT (xml
));
1572 process_window_get_process (ProcessWindow
*pwin
)
1574 return pwin
->process
;
1578 process_window_visible (ProcessWindow
*pwin
)
1580 return GTK_WIDGET_VISIBLE (pwin
->main_window
);
1584 process_window_show (ProcessWindow
*pwin
)
1586 if (!process_window_visible (pwin
))
1587 gtk_widget_show (pwin
->main_window
);
1589 gdk_window_show (pwin
->main_window
->window
);
1593 process_window_hide (ProcessWindow
*pwin
)
1595 if (process_window_visible (pwin
))
1596 hide_and_check_quit (pwin
->main_window
);
1602 GList
*toplevels
, *tmplist
;
1604 tmplist
= toplevels
= gtk_window_list_toplevels ();
1606 if (GTK_WIDGET_VISIBLE (toplevels
->data
))
1608 tmplist
= tmplist
->next
;
1611 g_list_free (toplevels
);
1617 hide_and_check_quit (GtkWidget
*window
)
1619 gtk_widget_hide (window
);
1627 process_window_maybe_detach (ProcessWindow
*pwin
)
1630 const char *message
;
1633 if (pwin
->process
->status
== MP_PROCESS_EXITING
)
1634 message
= _("Really detach from finished process?");
1636 message
= _("Really detach from running process?");
1638 dialog
= gtk_message_dialog_new (GTK_WINDOW (pwin
->main_window
),
1639 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1640 GTK_MESSAGE_QUESTION
,
1643 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_YES
);
1645 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1646 gtk_widget_destroy (dialog
);
1648 if (response
== GTK_RESPONSE_YES
)
1649 process_detach (pwin
->process
);
1654 process_window_maybe_kill (ProcessWindow
*pwin
)
1656 if (pwin
->process
->status
== MP_PROCESS_EXITING
)
1657 process_window_maybe_detach (pwin
);
1662 dialog
= gtk_message_dialog_new (GTK_WINDOW (pwin
->main_window
),
1663 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1664 GTK_MESSAGE_QUESTION
,
1666 _("Really kill running process?"));
1667 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
1670 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1671 gtk_widget_destroy (dialog
);
1673 if (response
== GTK_RESPONSE_YES
)
1674 process_kill (pwin
->process
);
1679 sigchld_handler (int signum
)
1681 int old_errno
= errno
;
1684 int pid
= waitpid (WAIT_ANY
, NULL
, WNOHANG
);
1685 if (pid
< 0 && errno
!= ECHILD
)
1686 g_error ("waitpid: %s\n", g_strerror (errno
));
1694 static char *profile_type_string
= NULL
;
1695 static char *profile_rate_string
= NULL
;
1696 static int profile_interval
= 1000;
1697 static gchar
**profile_skip_funcs
= NULL
;
1699 static const GOptionEntry entries
[] =
1701 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE
, &default_follow_fork
,
1702 N_("Create new windows for forked processes"), NULL
},
1703 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE
, &default_follow_exec
,
1704 N_("Retain windows for processes after exec()"), NULL
},
1705 { "profile", '\0', 0, G_OPTION_ARG_STRING
, &profile_type_string
,
1706 N_("Type of profiling information to collect"), "memory/cycles/time" },
1707 { "rate", '\0', 0, G_OPTION_ARG_STRING
, &profile_rate_string
,
1708 N_("Number of samples/sec for time profile (1k=1000)"), NULL
},
1709 { "skip-funcs", '\0', 0, G_OPTION_ARG_STRING_ARRAY
, &profile_skip_funcs
,
1710 N_("Functions allocating memory"), "function_name" },
1715 parse_options (int *argc
, char ***argv
)
1718 GOptionContext
*context
;
1720 context
= g_option_context_new ("- A memory profiler");
1722 g_option_context_add_main_entries (context
, entries
, GETTEXT_PACKAGE
);
1723 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
1724 g_option_context_parse (context
, argc
, argv
, &err
);
1728 initialize_skip_funcs ()
1732 skip_funcs
= g_slist_append (skip_funcs
, "g_malloc");
1733 skip_funcs
= g_slist_append (skip_funcs
, "g_malloc0");
1734 skip_funcs
= g_slist_append (skip_funcs
, "g_realloc");
1735 skip_funcs
= g_slist_append (skip_funcs
, "g_strdup");
1736 skip_funcs
= g_slist_append (skip_funcs
, "g_strndup");
1737 skip_funcs
= g_slist_append (skip_funcs
, "g_slice_alloc");
1738 skip_funcs
= g_slist_append (skip_funcs
, "g_slice_alloc0");
1739 skip_funcs
= g_slist_append (skip_funcs
, "strdup");
1740 skip_funcs
= g_slist_append (skip_funcs
, "strndup");
1741 skip_funcs
= g_slist_append (skip_funcs
, "_Znwj");
1742 skip_funcs
= g_slist_append (skip_funcs
, "_ZN3WTF16fastZeroedMallocEj");
1744 while (profile_skip_funcs
&& profile_skip_funcs
[i
]) {
1745 skip_funcs
= g_slist_append (skip_funcs
, profile_skip_funcs
[i
]);
1751 main(int argc
, char **argv
)
1753 ProcessWindow
*initial_window
;
1755 gtk_init (&argc
, &argv
);
1757 parse_options (&argc
, &argv
);
1759 /* Set up a handler for SIGCHLD to avoid zombie children
1761 signal (SIGCHLD
, sigchld_handler
);
1763 bindtextdomain (GETTEXT_PACKAGE
, LOCALEDIR
);
1764 textdomain (GETTEXT_PACKAGE
);
1766 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1767 bind_textdomain_codeset (GETTEXT_PACKAGE
, "UTF-8");
1770 /* If the user didn't specify the profile type explicitely,
1771 * we guess from the executable name.
1773 if (!profile_type_string
) {
1775 basename
= g_path_get_basename (argv
[0]);
1777 if (strcmp (basename
, "speedprof") == 0)
1778 profile_type_string
= "cycles";
1780 profile_type_string
= "memory";
1785 if (strcmp (profile_type_string
, "memory") == 0)
1786 profile_type
= MP_PROFILE_MEMORY
;
1787 else if (strcmp (profile_type_string
, "cycles") == 0)
1788 profile_type
= MP_PROFILE_CYCLES
;
1789 else if (strcmp (profile_type_string
, "time") == 0)
1790 profile_type
= MP_PROFILE_TIME
;
1792 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1796 initialize_skip_funcs();
1798 if (profile_rate_string
) {
1801 int len
= strlen (profile_rate_string
);
1802 char suffix
[2] = { '\0', '\0' };
1806 (profile_rate_string
[len
- 1] == 'k' ||
1807 profile_rate_string
[len
- 1] == 'K')) {
1808 suffix
[0] = profile_rate_string
[len
- 1];
1810 profile_rate_string
[len
- 1] = '\0';
1813 rate
= strtod (profile_rate_string
, &end
);
1814 if (len
== 0 || *end
!= '\0' ||
1815 rate
* multiplier
<= 1 || rate
* multiplier
> 1000000) {
1816 g_printerr ("Invalid rate: %s%s\n",
1817 profile_rate_string
, suffix
);
1821 profile_interval
= (int) (0.5 + 1000000 / (rate
* multiplier
));
1824 glade_file
= "./memprof.glade";
1825 if (!g_file_test (glade_file
, G_FILE_TEST_EXISTS
)) {
1826 glade_file
= g_build_filename (DATADIR
, "memprof.glade", NULL
);
1828 if (!g_file_test (glade_file
, G_FILE_TEST_EXISTS
)) {
1829 show_error (NULL
, ERROR_FATAL
, _("Cannot find memprof.glade"));
1832 global_server
= mp_server_new ();
1833 mp_server_set_profile_type (global_server
, profile_type
);
1834 mp_server_set_interval (global_server
, profile_interval
);
1836 g_signal_connect (global_server
, "process_created",
1837 G_CALLBACK (process_created_cb
), NULL
);
1839 initial_window
= process_window_new ();
1841 gtk_widget_show (initial_window
->main_window
);
1844 run_file (initial_window
, (char **)(argv
+ 1));