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)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35 #include <glade/glade.h>
39 #include "leakdetect.h"
45 #include "treeviewutils.h"
48 #include <glib/gi18n.h>
49 #include <glib/gprintf.h>
50 #include "elfparser.h"
52 struct _ProcessWindow
{
57 GtkWidget
*main_window
;
58 GtkWidget
*main_notebook
;
59 GtkWidget
*n_allocations_label
;
60 GtkWidget
*profile_status_label
;
61 GtkWidget
*bytes_per_label
;
62 GtkWidget
*total_bytes_label
;
64 GtkWidget
*profile_func_tree_view
;
65 GtkWidget
*profile_caller_tree_view
;
66 GtkWidget
*profile_descendants_tree_view
;
68 GtkWidget
*leak_block_tree_view
;
69 GtkWidget
*leak_stack_tree_view
;
71 GtkWidget
*usage_max_label
;
72 GtkWidget
*usage_area
;
78 guint status_update_timeout
;
96 PROFILE_DESCENDANTS_NAME
,
97 PROFILE_DESCENDANTS_SELF
,
98 PROFILE_DESCENDANTS_NONRECURSE
,
99 PROFILE_DESCENDANTS_SYMBOL
117 MPServer
*global_server
;
119 static ProcessWindow
*process_window_new (void);
120 static void process_window_destroy (ProcessWindow
*pwin
);
121 static void process_window_reset (ProcessWindow
*pwin
);
123 static GSList
*skip_funcs
= NULL
;
124 static GSList
*skip_regexes
= NULL
;
126 static gboolean default_follow_fork
= FALSE
;
127 static gboolean default_follow_exec
= FALSE
;
129 char *stack_command
= NULL
;
131 GSList
*process_windows
= NULL
;
133 static MPProfileType profile_type
;
136 /* Helper function to print a clear error message if there
137 * is a problem looking up a particular widget
140 get_widget (GladeXML
*glade
,
143 GtkWidget
*widget
= glade_xml_get_widget (glade
, name
);
145 g_error ("Cannot lookup %s\n", name
);
151 /************************************************************
153 ************************************************************/
156 draw_bar (GtkWidget
*widget
, int red
, int green
, int blue
,
159 GdkGC
*gc
= gdk_gc_new (widget
->window
);
166 gdk_gc_set_rgb_fg_color (gc
, &color
);
168 gdk_draw_rectangle (widget
->window
, gc
, TRUE
,
169 0, 0, size
, widget
->allocation
.height
);
175 gdk_gc_set_rgb_fg_color (gc
, &color
);
177 gdk_draw_rectangle (widget
->window
, gc
, FALSE
,
178 0, 0, size
- 1, widget
->allocation
.height
- 1);
184 on_usage_area_expose (GtkWidget
*widget
, GdkEvent
*event
,
187 ProcessWindow
*pwin
= data
;
194 width
= widget
->allocation
.width
;
197 draw_bar (widget
, 0xffff, 0xffff, 0xffff, width
);
201 bytes_used
= pwin
->process
->bytes_used
;
206 leak_size
= (width
* ((double)pwin
->usage_leaked
/ pwin
->usage_max
));
207 high_size
= (width
* ((double)pwin
->usage_high
/ pwin
->usage_max
));
208 current_size
= (width
* ((double)bytes_used
/ pwin
->usage_max
));
210 draw_bar (widget
, 0x0000, 0x0000, 0xffff, high_size
);
211 draw_bar (widget
, 0xffff, 0xffff, 0x0000, current_size
);
212 draw_bar (widget
, 0xffff, 0x0000, 0x0000, leak_size
);
218 update_status (gpointer data
)
221 ProcessWindow
*pwin
= (ProcessWindow
*)data
;
223 tmp
= g_strdup_printf ("%d", pwin
->process
->bytes_used
);
224 gtk_label_set_text (GTK_LABEL (pwin
->total_bytes_label
), tmp
);
227 tmp
= g_strdup_printf ("%d", pwin
->process
->n_allocations
);
228 gtk_label_set_text (GTK_LABEL (pwin
->n_allocations_label
), tmp
);
231 tmp
= g_strdup_printf ("%d samples", pwin
->process
->bytes_used
);
232 gtk_label_set_text (GTK_LABEL (pwin
->profile_status_label
), tmp
);
235 if (pwin
->process
->n_allocations
== 0)
238 tmp
= g_strdup_printf ("%.2f",
239 (double)pwin
->process
->bytes_used
/
240 pwin
->process
->n_allocations
);
241 gtk_label_set_text (GTK_LABEL (pwin
->bytes_per_label
), tmp
);
244 if (pwin
->process
->bytes_used
> pwin
->usage_max
) {
245 while ((pwin
->process
->bytes_used
> pwin
->usage_max
))
246 pwin
->usage_max
*= 2;
248 tmp
= g_strdup_printf ("%dk", pwin
->usage_max
/ 1024);
249 gtk_label_set_text (GTK_LABEL (pwin
->usage_max_label
), tmp
);
253 pwin
->usage_high
= MAX (pwin
->process
->bytes_used
, pwin
->usage_high
);
255 gtk_widget_queue_draw (pwin
->usage_area
);
261 /************************************************************
263 ************************************************************/
266 get_sort_info (GtkTreeView
*view
, int *sort_column
, GtkSortType
*sort_type
)
268 GtkTreeModel
*model
= gtk_tree_view_get_model (view
);
270 if (model
&& GTK_IS_TREE_SORTABLE (model
))
271 return gtk_tree_sortable_get_sort_column_id (
272 GTK_TREE_SORTABLE (model
), sort_column
, sort_type
);
278 profile_func_list_goto_symbol (ProcessWindow
*pwin
, char *symbol
)
280 GtkTreeModel
*function_list
;
282 gboolean found
= FALSE
;
284 function_list
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
286 if (gtk_tree_model_get_iter_first (function_list
, &iter
)) {
290 gtk_tree_model_get (function_list
, &iter
,
291 PROFILE_FUNC_FUNC
, &func
,
294 if (symbol_equal (func
->node
->symbol
, symbol
)) {
298 } while (gtk_tree_model_iter_next (function_list
, &iter
));
303 gtk_tree_model_get_path (function_list
, &iter
);
305 gtk_tree_view_set_cursor (
306 GTK_TREE_VIEW (pwin
->profile_func_tree_view
), path
, 0, FALSE
);
308 gtk_widget_grab_focus (GTK_WIDGET (pwin
->profile_func_tree_view
));
312 profile_descendants_row_activated (GtkTreeView
*treeview
,
314 GtkTreeViewColumn
*column
,
317 GtkTreeModel
*descendants_store
;
318 ProcessWindow
*pwin
= data
;
322 descendants_store
= gtk_tree_view_get_model (treeview
);
323 if (!gtk_tree_model_get_iter (descendants_store
, &iter
, path
))
326 gtk_tree_model_get (descendants_store
, &iter
, PROFILE_DESCENDANTS_SYMBOL
, &desc_symbol
, -1);
328 profile_func_list_goto_symbol (pwin
, desc_symbol
);
332 profile_caller_row_activated (GtkTreeView
*treeview
,
334 GtkTreeViewColumn
*column
,
337 GtkTreeModel
*caller_list
;
338 ProcessWindow
*pwin
= data
;
342 caller_list
= gtk_tree_view_get_model (treeview
);
343 if (!gtk_tree_model_get_iter (caller_list
, &iter
, path
))
346 gtk_tree_model_get (caller_list
, &iter
, 3, &caller_symbol
, -1);
348 if (caller_symbol
!= GINT_TO_POINTER (-1))
349 profile_func_list_goto_symbol (pwin
, caller_symbol
);
353 set_double (GtkTreeModel
*model
, GtkTreeIter
*iter
, int column
, double value
)
355 if (GTK_IS_TREE_STORE (model
))
356 gtk_tree_store_set (GTK_TREE_STORE (model
), iter
, column
, value
, -1);
358 gtk_list_store_set (GTK_LIST_STORE (model
), iter
, column
, value
, -1);
362 set_sample (GtkTreeModel
*model
, GtkTreeIter
*iter
, int column
, guint value
, guint n_samples
)
364 if (profile_type
== MP_PROFILE_MEMORY
)
365 set_double (model
, iter
, column
, value
);
367 set_double (model
, iter
, column
, 100 * (double)value
/ n_samples
);
371 add_node (GtkTreeStore
*store
, int n_samples
,
372 const GtkTreeIter
*parent
, ProfileDescendantTreeNode
*node
)
378 g_return_if_fail (GTK_IS_TREE_STORE (store
));
380 gtk_tree_store_insert (store
, &iter
, (GtkTreeIter
*)parent
, 0);
383 name
= elf_demangle (node
->symbol
);
385 name
= g_strdup ("???");
387 if (profile_type
== MP_PROFILE_MEMORY
) {
388 gtk_tree_store_set (store
, &iter
,
389 PROFILE_DESCENDANTS_NAME
, name
,
390 PROFILE_DESCENDANTS_SELF
, (double)node
->self
,
391 PROFILE_DESCENDANTS_NONRECURSE
, (double)node
->non_recursion
,
392 PROFILE_DESCENDANTS_SYMBOL
, node
->symbol
,
396 gtk_tree_store_set (store
, &iter
,
397 PROFILE_DESCENDANTS_NAME
, name
,
398 PROFILE_DESCENDANTS_SELF
, (100.0 * node
->self
) / n_samples
,
399 PROFILE_DESCENDANTS_NONRECURSE
, (100.0 * node
->non_recursion
) / n_samples
,
400 PROFILE_DESCENDANTS_SYMBOL
, node
->symbol
,
406 for (i
= 0; i
< node
->children
->len
; ++i
)
407 add_node (store
, n_samples
, &iter
, node
->children
->pdata
[i
]);
411 profile_selection_changed (GtkTreeSelection
*selection
, ProcessWindow
*pwin
)
414 GtkTreeIter selected
;
416 GtkTreeStore
*tree_store
;
417 GtkListStore
*list_store
;
418 GtkTreeModel
*list_model
;
420 GPtrArray
*caller_list
;
421 ProfileDescendantTree
*descendant_tree
;
423 int n_samples
= pwin
->profile
->n_bytes
;
425 GtkSortType old_sort_type
;
428 if (!gtk_tree_selection_get_selected (selection
, (GtkTreeModel
**)&store
, &selected
))
430 g_warning ("No selection");
434 gtk_tree_model_get (GTK_TREE_MODEL (store
), &selected
,
435 PROFILE_FUNC_FUNC
, &func
,
438 was_sorted
= get_sort_info (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
),
439 &old_sort_column
, &old_sort_type
);
441 /* fill descendants tree */
442 tree_store
= gtk_tree_store_new (4,
448 descendant_tree
= profile_func_create_descendant_tree (func
);
450 add_node (tree_store
, n_samples
, NULL
, descendant_tree
->roots
->pdata
[0]);
452 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), TRUE
);
454 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
),
455 GTK_TREE_MODEL (tree_store
));
457 /* Expand the toplevel of the descendant tree so we see the immediate
460 path
= gtk_tree_path_new_from_indices (0, -1);
461 gtk_tree_view_expand_row (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
), path
, FALSE
);
462 gtk_tree_path_free (path
);
465 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store
),
466 old_sort_column
, old_sort_type
);
468 gtk_tree_sortable_set_sort_column_id (
469 GTK_TREE_SORTABLE (tree_store
), 2, GTK_SORT_DESCENDING
);
471 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (tree_store
));
473 g_object_unref (G_OBJECT (tree_store
));
475 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
));
477 profile_descendant_tree_free (descendant_tree
);
479 /* fill caller tree */
480 was_sorted
= get_sort_info (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
),
481 &old_sort_column
, &old_sort_type
);
483 list_store
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_DOUBLE
, G_TYPE_DOUBLE
, G_TYPE_POINTER
);
484 list_model
= GTK_TREE_MODEL (list_store
);
486 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
488 caller_list
= profile_func_create_caller_list (func
);
490 for (i
= 0; i
< caller_list
->len
; ++i
) {
493 ProfileFunc
*caller
= caller_list
->pdata
[i
];
496 if (caller
->node
->symbol
)
497 name
= caller
->node
->symbol
;
502 name
= "<spontaneous>";
504 gtk_list_store_append (list_store
, &iter
);
506 gtk_list_store_set (list_store
, &iter
,
507 PROFILE_CALLER_NAME
, name
,
508 PROFILE_CALLER_SYMBOL
, caller
->node
? caller
->node
->symbol
: GINT_TO_POINTER (-1),
511 set_sample (list_model
, &iter
, PROFILE_CALLER_SELF
, caller
->self
, n_samples
);
512 set_sample (list_model
, &iter
, PROFILE_CALLER_TOTAL
, caller
->total
, n_samples
);
514 profile_caller_list_free (caller_list
);
516 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
518 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
),
522 gtk_tree_sortable_set_sort_column_id (
523 GTK_TREE_SORTABLE (list_store
), old_sort_column
, old_sort_type
);
525 gtk_tree_sortable_set_sort_column_id (
526 GTK_TREE_SORTABLE (list_store
), 2, GTK_SORT_DESCENDING
);
528 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (list_store
));
530 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
532 g_object_unref (G_OBJECT (list_store
));
534 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_caller_tree_view
), TRUE
);
538 profile_fill (ProcessWindow
*pwin
)
544 int n_samples
= pwin
->profile
->n_bytes
;
547 GtkSortType old_sort_type
;
551 get_sort_info (GTK_TREE_VIEW (pwin
->profile_func_tree_view
),
552 &old_sort_column
, &old_sort_type
);
554 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), NULL
);
555 store
= gtk_list_store_new (4, G_TYPE_STRING
, G_TYPE_DOUBLE
, G_TYPE_DOUBLE
, G_TYPE_POINTER
);
556 model
= GTK_TREE_MODEL (store
);
558 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), model
);
560 /* inserting in a ListStore is O(n) when sorting ... */
561 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
563 for (i
= 0; i
< pwin
->profile
->functions
->len
; ++i
) {
567 ProfileFunc
*func
= pwin
->profile
->functions
->pdata
[i
];
569 gtk_list_store_append (store
, &iter
);
573 if (func
->node
->symbol
)
574 name
= func
->node
->symbol
;
578 gtk_list_store_set (store
, &iter
,
579 PROFILE_FUNC_NAME
, name
,
580 PROFILE_FUNC_FUNC
, func
,
583 set_sample (model
, &iter
, PROFILE_FUNC_SELF
, func
->self
, n_samples
);
584 set_sample (model
, &iter
, PROFILE_FUNC_TOTAL
, func
->total
, n_samples
);
587 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
590 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store
), old_sort_column
,
594 gtk_tree_sortable_set_sort_column_id (
595 GTK_TREE_SORTABLE (store
), 2, GTK_SORT_DESCENDING
);
598 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (store
));
600 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_func_tree_view
), TRUE
);
602 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
604 g_object_unref (G_OBJECT (store
));
608 /************************************************************
609 * GUI for leak detection
610 ************************************************************/
613 leak_block_get_selected (ProcessWindow
*pwin
)
615 GtkTreeSelection
*selection
;
620 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
621 if (selection
&& gtk_tree_selection_get_selected (selection
, &model
, &iter
))
622 gtk_tree_model_get (model
, &iter
, LEAK_BLOCK_BLOCK
, &block
, -1);
628 leak_block_selection_changed (GtkTreeSelection
*selection
,
631 GtkTreeModel
*model
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
632 GtkListStore
*store
= GTK_LIST_STORE (model
);
633 Block
*block
= leak_block_get_selected (pwin
);
636 gtk_list_store_clear (store
);
641 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
643 for (stack
= block
->stack
; stack
!= NULL
; stack
= stack
->parent
) {
645 const char *filename
;
649 if (!process_find_line (pwin
->process
, stack
->address
,
650 &filename
, &functionname
, &line
)) {
651 /* 0x3f == '?' -- suppress trigraph warnings */
652 functionname
= g_strdup ("(\x3f\x3f\x3f)");
653 filename
= "(\x3f\x3f\x3f)";
657 gtk_list_store_append (store
, &iter
);
658 gtk_list_store_set (store
, &iter
,
659 LEAK_STACK_NAME
, functionname
,
660 LEAK_STACK_LINE
, line
,
661 LEAK_STACK_FILE
, filename
,
665 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
669 leaks_fill (ProcessWindow
*pwin
)
675 model
= gtk_tree_view_get_model (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
676 store
= GTK_LIST_STORE (model
);
678 gtk_list_store_clear (store
);
680 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
682 tmp_list
= pwin
->leaks
;
684 const char *filename
;
685 char *functionname
= NULL
;
690 Block
*block
= tmp_list
->data
;
693 for (stack
= block
->stack
; stack
!= NULL
; stack
= stack
->parent
) {
694 if (process_find_line (pwin
->process
, stack
->address
,
695 &filename
, &functionname
, &line
)) {
701 for (tmp_list
= skip_funcs
; tmp_list
!= NULL
; tmp_list
= tmp_list
->next
) {
702 if (!strcmp (functionname
, tmp_list
->data
)) {
712 for (tmp_list
= skip_regexes
; tmp_list
!= NULL
; tmp_list
= tmp_list
->next
) {
715 regcomp (®ex
, tmp_list
->data
, 0);
717 if (!regexec (®ex
, functionname
, 0, NULL
, 0)) {
732 functionname
= g_strdup ("(\x3f\x3f\x3f)");
734 gtk_list_store_append (store
, &iter
);
735 gtk_list_store_set (store
, &iter
,
736 LEAK_BLOCK_ADDR
, block
->addr
,
737 LEAK_BLOCK_SIZE
, block
->size
,
738 LEAK_BLOCK_CALLER
, functionname
,
739 LEAK_BLOCK_BLOCK
, block
,
742 g_free (functionname
);
744 tmp_list
= tmp_list
->next
;
747 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
751 leak_stack_run_command (ProcessWindow
*pwin
, Block
*block
, int frame
)
753 const char *filename
;
757 StackNode
*stack
= block
->stack
;
759 stack
= stack
->parent
;
761 if (process_find_line (pwin
->process
, stack
->address
,
762 &filename
, &functionname
, &line
)) {
764 GString
*command
= g_string_new (NULL
);
765 char *p
= stack_command
;
774 g_string_append (command
, filename
);
777 snprintf(buf
, 32, "%d", line
);
778 g_string_append (command
, buf
);
781 g_string_append_c (command
, '%');
784 g_string_append_c (command
, '%');
785 g_string_append_c (command
, *p
);
788 g_string_append_c (command
, *p
);
794 cmdline
= g_strdup_printf ("/bin/sh -c %s", command
->str
);
796 if (!g_spawn_command_line_async (cmdline
, &err
)) {
797 show_error (pwin
->main_window
,
798 ERROR_MODAL
, _("Executation of \"%s\" failed: %s"),
799 command
->str
, err
->message
);
806 g_string_free (command
, FALSE
);
811 leak_stack_row_activated (GtkTreeView
*tree_view
,
821 model
= gtk_tree_view_get_model (tree_view
);
822 gtk_tree_model_get_iter (model
, &iter
, path
);
823 frame
= list_iter_get_index (model
, &iter
);
825 block
= leak_block_get_selected (pwin
);
827 leak_stack_run_command (pwin
, block
, frame
);
832 /************************************************************
833 * File Selection handling
834 ************************************************************/
837 filename_ok_clicked (GtkWidget
*button
, gchar
**name
)
839 GtkWidget
*fs
= gtk_widget_get_ancestor (button
,
840 gtk_file_selection_get_type());
842 *name
= g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs
)));
843 gtk_widget_destroy (fs
);
847 get_filename (const gchar
*title
,
848 const gchar
*prompt_text
,
849 const gchar
*suggested_name
)
852 gchar
*filename
= NULL
;
854 fs
= gtk_file_selection_new (title
);
855 gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs
),
858 gtk_label_set_text (GTK_LABEL (GTK_FILE_SELECTION (fs
)->selection_text
),
860 g_signal_connect (GTK_FILE_SELECTION (fs
)->ok_button
, "clicked",
861 G_CALLBACK (filename_ok_clicked
), &filename
);
862 g_signal_connect_swapped (GTK_FILE_SELECTION (fs
)->cancel_button
, "clicked",
863 G_CALLBACK (gtk_widget_destroy
), fs
);
864 g_signal_connect (fs
, "destroy",
865 G_CALLBACK (gtk_main_quit
), NULL
);
867 gtk_widget_show (fs
);
873 /* Really ugly utility function to retrieve the ProcessWindow from
874 * either a menu_item or toolbar item.
877 pwin_from_widget (GtkWidget
*widget
)
881 if (GTK_IS_MENU_ITEM (widget
)) {
882 GtkWidget
*menu_shell
= widget
->parent
;
884 while (menu_shell
&& !GTK_IS_MENU_BAR (menu_shell
)) {
885 menu_shell
= gtk_menu_get_attach_widget (GTK_MENU (menu_shell
))->parent
;
887 g_assert (menu_shell
!= NULL
);
889 app
= gtk_widget_get_toplevel (menu_shell
);
891 app
= gtk_widget_get_toplevel (widget
);
893 return g_object_get_data (G_OBJECT (app
), "process-window");
897 close_cb (GtkWidget
*widget
)
899 ProcessWindow
*pwin
= pwin_from_widget (widget
);
901 hide_and_check_quit (pwin
->main_window
);
905 exit_cb (GtkWidget
*widget
)
911 reset_cb (MPProcess
*process
, ProcessWindow
*pwin
)
913 process_window_reset (pwin
);
917 status_changed_cb (MPProcess
*process
, ProcessWindow
*pwin
)
919 if (process
->status
== MP_PROCESS_DEFUNCT
||
920 process
->status
== MP_PROCESS_DETACHED
) {
922 if (g_slist_length (process_windows
) > 1) {
923 tree_window_remove (pwin
);
924 process_window_destroy (pwin
);
926 tree_window_remove (pwin
);
929 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin
->process
), G_CALLBACK (status_changed_cb
), pwin
);
930 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin
->process
), G_CALLBACK (reset_cb
), pwin
);
931 g_object_unref (G_OBJECT (pwin
->process
));
932 pwin
->process
= NULL
;
935 if (pwin
->status_update_timeout
) {
936 g_source_remove (pwin
->status_update_timeout
);
937 pwin
->status_update_timeout
= 0;
940 process_window_reset (pwin
);
944 char *status
= process_get_status_text (process
);
945 char *cmdline
= process_get_cmdline (process
);
946 char *title
= g_strdup_printf ("%s - %s (%d) - %s", _("MemProf"), cmdline
, process
->pid
, status
);
947 gtk_window_set_title (GTK_WINDOW (pwin
->main_window
), title
);
956 list_view_clear (GtkTreeView
*tree_view
)
958 GtkTreeModel
*model
= gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view
));
959 gtk_list_store_clear (GTK_LIST_STORE (model
));
963 process_window_reset (ProcessWindow
*pwin
)
966 profile_free (pwin
->profile
);
967 pwin
->profile
= NULL
;
969 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_func_tree_view
), NULL
);
970 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_caller_tree_view
), NULL
);
971 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
), NULL
);
973 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_func_tree_view
), FALSE
);
974 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_caller_tree_view
), FALSE
);
975 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), FALSE
);
979 g_slist_free (pwin
->leaks
);
981 list_view_clear (GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
982 list_view_clear (GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
985 pwin
->usage_max
= 32*1024;
986 pwin
->usage_high
= 0;
987 pwin
->usage_leaked
= 0;
989 gtk_window_set_title (GTK_WINDOW (pwin
->main_window
), "MemProf");
991 gtk_widget_queue_draw (pwin
->usage_area
);
995 init_process (ProcessWindow
*pwin
, MPProcess
*process
)
997 pwin
->process
= process
;
999 process_set_follow_fork (pwin
->process
, default_follow_fork
);
1000 process_set_follow_exec (pwin
->process
, default_follow_exec
);
1002 pwin
->status_update_timeout
=
1007 g_signal_connect (process
, "status_changed",
1008 G_CALLBACK (status_changed_cb
), pwin
);
1009 g_signal_connect (process
, "reset",
1010 G_CALLBACK (reset_cb
), pwin
);
1012 tree_window_add (pwin
);
1016 process_created_cb (MPServer
*server
, MPProcess
*process
)
1018 ProcessWindow
*pwin
= process_window_new ();
1020 init_process (pwin
, process
);
1022 tree_window_show ();
1026 run_file (ProcessWindow
*pwin
, char **args
)
1031 g_return_val_if_fail (args
!= NULL
, FALSE
);
1032 g_return_val_if_fail (args
[0] != NULL
, FALSE
);
1034 path
= process_find_exec (args
);
1037 g_warning ("Process new '%s'\n", path
);
1038 MPProcess
*process
= process_new (global_server
);
1039 process_run (process
, path
, args
);
1041 if (pwin
->process
) {
1042 pwin
= process_window_new ();
1043 tree_window_show ();
1046 init_process (pwin
, process
);
1048 gtk_widget_show (pwin
->main_window
);
1053 show_error (pwin
->main_window
,
1055 _("Cannot find executable for \"%s\""),
1066 run_cb (GtkWidget
*widget
)
1069 GtkWidget
*run_dialog
;
1072 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1074 xml
= glade_xml_new (glade_file
, "RunDialog", NULL
);
1075 run_dialog
= get_widget (xml
, "RunDialog");
1076 entry
= get_widget (xml
, "RunDialog-entry");
1078 g_signal_connect_swapped (entry
, "activate",
1079 G_CALLBACK (gtk_widget_activate
),
1080 get_widget (xml
, "RunDialog-run"));
1082 g_object_unref (G_OBJECT (xml
));
1085 gtk_window_set_transient_for (GTK_WINDOW (run_dialog
),
1086 GTK_WINDOW (pwin
->main_window
));
1087 if (gtk_dialog_run (GTK_DIALOG (run_dialog
)) == 1) {
1092 text
= gtk_editable_get_chars (GTK_EDITABLE (entry
), 0, -1);
1093 args
= process_parse_exec (text
);
1095 result
= run_file (pwin
, args
);
1107 gtk_widget_destroy (run_dialog
);
1111 kill_cb (GtkWidget
*widget
)
1113 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1116 process_window_maybe_kill (pwin
);
1120 detach_cb (GtkWidget
*widget
)
1122 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1125 process_window_maybe_detach (pwin
);
1129 process_tree_cb (GtkWidget
*widget
)
1131 tree_window_show ();
1135 save_leak_cb (GtkWidget
*widget
)
1137 static gchar
*suggestion
= NULL
;
1140 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1143 filename
= get_filename ("Save Leak Report", "Output file",
1144 suggestion
? suggestion
: "memprof.leak");
1146 g_free (suggestion
);
1147 suggestion
= filename
;
1149 leaks_print (pwin
->process
, pwin
->leaks
, filename
);
1155 save_profile_cb (GtkWidget
*widget
)
1157 static gchar
*suggestion
= NULL
;
1160 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1162 if (pwin
->profile
) {
1163 filename
= get_filename ("Save Profile", "Output file",
1164 suggestion
? suggestion
: "memprof.out");
1166 g_free (suggestion
);
1167 suggestion
= filename
;
1169 profile_write (pwin
->profile
, filename
);
1175 save_current_cb (GtkWidget
*widget
)
1177 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1179 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin
->main_notebook
))) {
1181 save_profile_cb (widget
);
1184 save_leak_cb (widget
);
1190 generate_leak_cb (GtkWidget
*widget
)
1193 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1196 if (pwin
->process
) {
1197 process_stop_input (pwin
->process
);
1199 while (pwin
->leaks
) {
1200 block_unref(pwin
->leaks
->data
);
1201 pwin
->leaks
= g_slist_delete_link(pwin
->leaks
, pwin
->leaks
);
1203 pwin
->leaks
= leaks_find (pwin
->process
);
1205 pwin
->usage_leaked
= 0;
1206 tmp_list
= pwin
->leaks
;
1208 pwin
->usage_leaked
+= ((Block
*)tmp_list
->data
)->size
;
1209 tmp_list
= tmp_list
->next
;
1213 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin
->main_notebook
), 1);
1215 process_start_input (pwin
->process
);
1220 generate_profile_cb (GtkWidget
*widget
)
1222 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1224 if (pwin
->process
) {
1225 process_stop_input (pwin
->process
);
1227 if (pwin
->profile
) {
1228 profile_free (pwin
->profile
);
1229 pwin
->profile
= NULL
;
1232 pwin
->profile
= profile_create (pwin
->process
, skip_funcs
);
1233 process_start_input (pwin
->process
);
1234 profile_fill (pwin
);
1236 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin
->main_notebook
), 0);
1241 reset_profile_cb (GtkWidget
*widget
)
1243 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1245 process_window_reset (pwin
);
1248 process_clear_input (pwin
->process
);
1252 record_button_toggled_cb (GtkWidget
*widget
)
1254 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1256 if (gtk_toggle_tool_button_get_active
1257 (GTK_TOGGLE_TOOL_BUTTON (widget
)))
1258 process_start_input (pwin
->process
);
1260 process_stop_input (pwin
->process
);
1264 about_cb (GtkWidget
*widget
)
1266 #define OSLASH "\303\270"
1267 ProcessWindow
*pwin
= pwin_from_widget (widget
);
1269 /* FIXME: restore credits */
1270 gtk_show_about_dialog (GTK_WINDOW (pwin
->main_window
),
1276 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1277 "Copyright 2002, Kristian Rietveld\n"
1278 "Copyright 2002, 2006, 2007, S"OSLASH
"ren Sandmann",
1280 "copyright", "Copyright 2004-2006, S"OSLASH
"ren Sandmann",
1282 "version", PACKAGE_VERSION
,
1287 show_error_response (GtkDialog
*dialog
,
1291 if (response_id
== GTK_RESPONSE_OK
)
1292 gtk_widget_destroy (GTK_WIDGET (dialog
));
1296 show_error (GtkWidget
*parent_window
,
1298 const gchar
*format
,
1305 va_start (args
, format
);
1306 g_vasprintf (&message
, format
, args
);
1309 dialog
= gtk_message_dialog_new (parent_window
? GTK_WINDOW (parent_window
) : NULL
,
1310 GTK_DIALOG_DESTROY_WITH_PARENT
,
1311 (error
== ERROR_FATAL
) ?
1313 GTK_MESSAGE_WARNING
,
1314 GTK_BUTTONS_OK
, "%s", message
);
1317 gtk_window_set_title (GTK_WINDOW (dialog
),
1318 (error
== ERROR_FATAL
) ?
1319 _("MemProf Error") : _("MemProf Warning"));
1321 if (error
== ERROR_WARNING
) {
1322 gtk_widget_show (dialog
);
1323 g_signal_connect (dialog
, "response",
1324 G_CALLBACK (show_error_response
), NULL
);
1326 gtk_dialog_run (GTK_DIALOG (dialog
));
1327 gtk_widget_destroy (dialog
);
1330 if (error
== ERROR_FATAL
)
1335 process_window_free (ProcessWindow
*pwin
)
1337 /* FIXME: we leak the process structure */
1340 g_slist_free (pwin
->leaks
);
1343 profile_free (pwin
->profile
);
1345 process_windows
= g_slist_remove (process_windows
, pwin
);
1346 if (!process_windows
)
1354 process_window_destroy (ProcessWindow
*pwin
)
1356 if (pwin
->status_update_timeout
)
1357 g_source_remove (pwin
->status_update_timeout
);
1359 gtk_widget_destroy (pwin
->main_window
);
1363 static GtkTreeViewColumn
*
1364 add_sample_column (GtkTreeView
*view
, const gchar
*title
, gint model_column
)
1368 if (profile_type
== MP_PROFILE_MEMORY
)
1373 return add_double_format_column (view
, title
, model_column
, format
);
1377 setup_profile_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1379 GtkTreeSelection
*selection
;
1380 GtkTreeViewColumn
*col
;
1382 col
= add_plain_text_column (tree_view
, _("Functions"), PROFILE_FUNC_NAME
);
1383 add_sample_column (tree_view
, _("Self"), PROFILE_FUNC_SELF
);
1384 add_sample_column (tree_view
, _("Total"), PROFILE_FUNC_TOTAL
);
1385 gtk_tree_view_column_set_expand (col
, TRUE
);
1387 selection
= gtk_tree_view_get_selection (tree_view
);
1388 g_return_if_fail (selection
!= NULL
);
1389 g_signal_connect (selection
, "changed", G_CALLBACK (profile_selection_changed
), pwin
);
1391 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1395 setup_profile_descendants_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1397 GtkTreeViewColumn
*col
;
1399 col
= add_plain_text_column (tree_view
, _("Descendants"), PROFILE_DESCENDANTS_NAME
);
1400 add_sample_column (tree_view
, _("Self"), PROFILE_DESCENDANTS_SELF
);
1401 add_sample_column (tree_view
, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE
);
1402 gtk_tree_view_column_set_expand (col
, TRUE
);
1404 gtk_widget_set_sensitive (GTK_WIDGET (pwin
->profile_descendants_tree_view
), FALSE
);
1406 g_signal_connect (tree_view
, "row-activated",
1407 G_CALLBACK (profile_descendants_row_activated
), pwin
);
1409 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1413 setup_profile_caller_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1415 GtkTreeViewColumn
*col
;
1417 col
= add_plain_text_column (tree_view
, _("Callers"), PROFILE_CALLER_NAME
);
1418 add_sample_column (tree_view
, _("Self"), PROFILE_CALLER_SELF
);
1419 add_sample_column (tree_view
, _("Total"), PROFILE_CALLER_TOTAL
);
1420 gtk_tree_view_column_set_expand (col
, TRUE
);
1422 g_signal_connect (tree_view
, "row-activated",
1423 G_CALLBACK (profile_caller_row_activated
), pwin
);
1425 gtk_widget_set_sensitive (GTK_WIDGET (tree_view
), FALSE
);
1429 setup_leak_block_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1431 GtkTreeSelection
*selection
= gtk_tree_view_get_selection (tree_view
);
1432 GtkListStore
*store
;
1434 g_return_if_fail (selection
!= NULL
);
1436 store
= gtk_list_store_new (4,
1442 gtk_tree_view_set_model (tree_view
, GTK_TREE_MODEL (store
));
1444 add_pointer_column (tree_view
, _("Address"), LEAK_BLOCK_ADDR
);
1445 add_plain_text_column (tree_view
, _("Size"), LEAK_BLOCK_SIZE
);
1446 add_plain_text_column (tree_view
, _("Caller"), LEAK_BLOCK_CALLER
);
1448 g_signal_connect (selection
, "changed",
1449 G_CALLBACK (leak_block_selection_changed
), pwin
);
1451 gtk_tree_view_columns_autosize (tree_view
);
1455 setup_leak_stack_tree_view (ProcessWindow
*pwin
, GtkTreeView
*tree_view
)
1457 GtkListStore
*store
;
1459 store
= gtk_list_store_new (3,
1464 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view
), GTK_TREE_MODEL (store
));
1466 add_plain_text_column (tree_view
, _("Function"), LEAK_STACK_NAME
);
1467 add_plain_text_column (tree_view
, _("Line"), LEAK_STACK_LINE
);
1468 add_plain_text_column (tree_view
, _("File"), LEAK_STACK_FILE
);
1470 g_signal_connect (tree_view
, "row-activated",
1471 G_CALLBACK (leak_stack_row_activated
), pwin
);
1473 gtk_tree_view_columns_autosize (tree_view
);
1477 set_default_size (GtkWindow
*window
)
1481 GdkRectangle monitor
;
1483 GtkWidget
*widget
= GTK_WIDGET (window
);
1485 screen
= gtk_widget_get_screen (widget
);
1486 monitor_num
= gdk_screen_get_monitor_at_window (screen
, widget
->window
);
1488 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
1490 width
= monitor
.width
* 3 / 4;
1491 height
= monitor
.height
* 3 / 4;
1493 gtk_window_resize (window
, width
, height
);
1496 static ProcessWindow
*
1497 process_window_new (void)
1499 gchar
*fullfilename
;
1503 ProcessWindow
*pwin
;
1506 pwin
= g_new0 (ProcessWindow
, 1);
1507 process_windows
= g_slist_prepend (process_windows
, pwin
);
1509 pwin
->process
= NULL
;
1510 pwin
->profile
= NULL
;
1513 pwin
->usage_max
= 32*1024;
1514 pwin
->usage_high
= 0;
1515 pwin
->usage_leaked
= 0;
1517 xml
= glade_xml_new (glade_file
, "MainWindow", NULL
);
1519 pwin
->main_window
= get_widget (xml
, "MainWindow");
1520 gtk_widget_realize (pwin
->main_window
);
1522 fullfilename
= g_strdup ("./memprof.png");
1524 if (!g_file_test (fullfilename
, G_FILE_TEST_EXISTS
))
1525 fullfilename
= g_build_filename (DATADIR
, "memprof.png", NULL
);
1528 gtk_window_set_icon_from_file (GTK_WINDOW (pwin
->main_window
), fullfilename
, &err
);
1530 g_free (fullfilename
);
1532 g_signal_connect (pwin
->main_window
, "delete_event",
1533 G_CALLBACK (hide_and_check_quit
), pwin
);
1536 set_default_size (GTK_WINDOW (pwin
->main_window
));
1538 g_object_set_data_full (G_OBJECT (pwin
->main_window
),
1540 pwin
, (GDestroyNotify
)process_window_free
);
1542 pwin
->main_notebook
= get_widget (xml
, "main-notebook");
1544 pwin
->n_allocations_label
= get_widget (xml
, "n-allocations-label");
1545 pwin
->bytes_per_label
= get_widget (xml
, "bytes-per-label");
1546 pwin
->total_bytes_label
= get_widget (xml
, "total-bytes-label");
1548 pwin
->profile_status_label
= get_widget (xml
, "profile-status-label");
1550 /* setup profile tree views */
1551 pwin
->profile_func_tree_view
= get_widget (xml
, "profile-func-tree-view");
1552 pwin
->profile_descendants_tree_view
=
1553 get_widget (xml
, "profile-descendants-tree-view");
1554 pwin
->profile_caller_tree_view
= get_widget (xml
, "profile-caller-tree-view");
1556 setup_profile_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_func_tree_view
));
1557 setup_profile_descendants_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_descendants_tree_view
));
1558 setup_profile_caller_tree_view (pwin
, GTK_TREE_VIEW (pwin
->profile_caller_tree_view
));
1560 /* leak tree views */
1561 pwin
->leak_block_tree_view
= get_widget (xml
, "leak-block-tree-view");
1562 pwin
->leak_stack_tree_view
= get_widget (xml
, "leak-stack-tree-view");
1564 setup_leak_block_tree_view (pwin
, GTK_TREE_VIEW (pwin
->leak_block_tree_view
));
1565 setup_leak_stack_tree_view (pwin
, GTK_TREE_VIEW (pwin
->leak_stack_tree_view
));
1567 pwin
->usage_max_label
= get_widget (xml
, "usage-max-label");
1568 pwin
->usage_area
= get_widget (xml
, "usage-area");
1570 g_signal_connect (pwin
->usage_area
, "expose_event",
1571 G_CALLBACK (on_usage_area_expose
), pwin
);
1573 vpaned
= get_widget (xml
, "profile-vpaned");
1574 gtk_paned_set_position (GTK_PANED (vpaned
), 150);
1576 hpaned
= get_widget (xml
, "profile-hpaned");
1577 gtk_paned_set_position (GTK_PANED (hpaned
), 150);
1579 vpaned
= get_widget (xml
, "leaks-vpaned");
1580 gtk_paned_set_position (GTK_PANED (vpaned
), 150);
1582 /* If profiling time, not memory, hide all GUI related to leak
1585 if (profile_type
!= MP_PROFILE_MEMORY
) {
1586 gtk_widget_hide (get_widget (xml
, "leaks-vpaned"));
1587 gtk_widget_hide (get_widget (xml
, "toolbar-leaks-button"));
1588 gtk_widget_hide (get_widget (xml
, "save-leak-info"));
1589 gtk_widget_hide (get_widget (xml
, "generate-leak-report"));
1590 gtk_widget_hide (get_widget (xml
, "allocation-bar"));
1591 gtk_notebook_set_show_tabs (
1592 GTK_NOTEBOOK (get_widget (xml
, "main-notebook")), FALSE
);
1595 gtk_widget_hide (get_widget (xml
, "profile-status-label"));
1596 gtk_widget_hide (get_widget (xml
, "reset-profile-button"));
1599 glade_xml_signal_autoconnect (xml
);
1600 g_object_unref (G_OBJECT (xml
));
1607 process_window_get_process (ProcessWindow
*pwin
)
1609 return pwin
->process
;
1613 process_window_visible (ProcessWindow
*pwin
)
1615 return GTK_WIDGET_VISIBLE (pwin
->main_window
);
1619 process_window_show (ProcessWindow
*pwin
)
1621 if (!process_window_visible (pwin
))
1622 gtk_widget_show (pwin
->main_window
);
1624 gdk_window_show (pwin
->main_window
->window
);
1628 process_window_hide (ProcessWindow
*pwin
)
1630 if (process_window_visible (pwin
))
1631 hide_and_check_quit (pwin
->main_window
);
1637 GList
*toplevels
, *tmplist
;
1639 tmplist
= toplevels
= gtk_window_list_toplevels ();
1641 if (GTK_WIDGET_VISIBLE (toplevels
->data
))
1643 tmplist
= tmplist
->next
;
1646 g_list_free (toplevels
);
1652 hide_and_check_quit (GtkWidget
*window
)
1654 gtk_widget_hide (window
);
1662 process_window_maybe_detach (ProcessWindow
*pwin
)
1665 const char *message
;
1668 if (pwin
->process
->status
== MP_PROCESS_EXITING
)
1669 message
= _("Really detach from finished process?");
1671 message
= _("Really detach from running process?");
1673 dialog
= gtk_message_dialog_new (GTK_WINDOW (pwin
->main_window
),
1674 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1675 GTK_MESSAGE_QUESTION
,
1678 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_YES
);
1680 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1681 gtk_widget_destroy (dialog
);
1683 if (response
== GTK_RESPONSE_YES
)
1684 process_detach (pwin
->process
);
1689 process_window_maybe_kill (ProcessWindow
*pwin
)
1691 if (pwin
->process
->status
== MP_PROCESS_EXITING
)
1692 process_window_maybe_detach (pwin
);
1697 dialog
= gtk_message_dialog_new (GTK_WINDOW (pwin
->main_window
),
1698 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
1699 GTK_MESSAGE_QUESTION
,
1701 _("Really kill running process?"));
1702 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
1705 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1706 gtk_widget_destroy (dialog
);
1708 if (response
== GTK_RESPONSE_YES
)
1709 process_kill (pwin
->process
);
1714 sigchld_handler (int signum
)
1716 int old_errno
= errno
;
1719 int pid
= waitpid (WAIT_ANY
, NULL
, WNOHANG
);
1720 if (pid
< 0 && errno
!= ECHILD
)
1721 g_error ("waitpid: %s\n", g_strerror (errno
));
1729 static char *profile_type_string
= NULL
;
1730 static char *profile_rate_string
= NULL
;
1731 static int profile_interval
= 1000;
1733 static const GOptionEntry entries
[] =
1735 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE
, &default_follow_fork
,
1736 N_("Create new windows for forked processes"), NULL
},
1737 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE
, &default_follow_exec
,
1738 N_("Retain windows for processes after exec()"), NULL
},
1739 { "profile", '\0', 0, G_OPTION_ARG_STRING
, &profile_type_string
,
1740 N_("Type of profiling information to collect"), "memory/cycles/time" },
1741 { "rate", '\0', 0, G_OPTION_ARG_STRING
, &profile_rate_string
,
1742 N_("Number of samples/sec for time profile (1k=1000)"), NULL
},
1747 parse_options (int *argc
, char ***argv
)
1750 GOptionContext
*context
;
1752 context
= g_option_context_new ("- A memory profiler");
1754 g_option_context_add_main_entries (context
, entries
, GETTEXT_PACKAGE
);
1755 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
1756 g_option_context_parse (context
, argc
, argv
, &err
);
1760 main(int argc
, char **argv
)
1762 ProcessWindow
*initial_window
;
1764 gtk_init (&argc
, &argv
);
1766 parse_options (&argc
, &argv
);
1768 /* Set up a handler for SIGCHLD to avoid zombie children
1770 signal (SIGCHLD
, sigchld_handler
);
1772 bindtextdomain (GETTEXT_PACKAGE
, LOCALEDIR
);
1773 textdomain (GETTEXT_PACKAGE
);
1775 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1776 bind_textdomain_codeset (GETTEXT_PACKAGE
, "UTF-8");
1779 /* If the user didn't specify the profile type explicitely,
1780 * we guess from the executable name.
1782 if (!profile_type_string
) {
1784 basename
= g_path_get_basename (argv
[0]);
1786 if (strcmp (basename
, "speedprof") == 0)
1787 profile_type_string
= "cycles";
1789 profile_type_string
= "memory";
1794 if (strcmp (profile_type_string
, "memory") == 0)
1795 profile_type
= MP_PROFILE_MEMORY
;
1796 else if (strcmp (profile_type_string
, "cycles") == 0)
1797 profile_type
= MP_PROFILE_CYCLES
;
1798 else if (strcmp (profile_type_string
, "time") == 0)
1799 profile_type
= MP_PROFILE_TIME
;
1801 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1805 if (profile_rate_string
) {
1808 int len
= strlen (profile_rate_string
);
1809 char suffix
[2] = { '\0', '\0' };
1813 (profile_rate_string
[len
- 1] == 'k' ||
1814 profile_rate_string
[len
- 1] == 'K')) {
1815 suffix
[0] = profile_rate_string
[len
- 1];
1817 profile_rate_string
[len
- 1] = '\0';
1820 rate
= strtod (profile_rate_string
, &end
);
1821 if (len
== 0 || *end
!= '\0' ||
1822 rate
* multiplier
<= 1 || rate
* multiplier
> 1000000) {
1823 g_printerr ("Invalid rate: %s%s\n",
1824 profile_rate_string
, suffix
);
1828 profile_interval
= (int) (0.5 + 1000000 / (rate
* multiplier
));
1831 glade_file
= "./memprof.glade";
1832 if (!g_file_test (glade_file
, G_FILE_TEST_EXISTS
)) {
1833 glade_file
= g_build_filename (DATADIR
, "memprof.glade", NULL
);
1835 if (!g_file_test (glade_file
, G_FILE_TEST_EXISTS
)) {
1836 show_error (NULL
, ERROR_FATAL
, _("Cannot find memprof.glade"));
1839 global_server
= mp_server_new ();
1840 mp_server_set_profile_type (global_server
, profile_type
);
1841 mp_server_set_interval (global_server
, profile_interval
);
1843 g_signal_connect (global_server
, "process_created",
1844 G_CALLBACK (process_created_cb
), NULL
);
1846 initial_window
= process_window_new ();
1848 gtk_widget_show (initial_window
->main_window
);
1851 run_file (initial_window
, (char **)(argv
+ 1));