main.c: Fix a crash when clearing a profile
[memprof.git] / src / main.c
blob02b19edb22da06e45a2c0a0914c1ab7fb9853617
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.
23 /*====*/
25 #define _GNU_SOURCE
27 #include "config.h"
29 #include <stdarg.h>
30 #include <stdio.h>
32 #include <errno.h>
33 #include <sys/wait.h>
34 #include <signal.h>
36 #include <glade/glade.h>
38 #include <regex.h>
40 #include "gui.h"
41 #include "memprof.h"
42 #include "treeviewutils.h"
43 #include <string.h>
44 #include <stdlib.h>
45 #include <glib/gi18n.h>
46 #include <glib/gprintf.h>
47 #include "elfparser.h"
50 enum {
51 PROFILE_FUNC_NAME,
52 PROFILE_FUNC_SELF,
53 PROFILE_FUNC_TOTAL,
54 PROFILE_FUNC_FUNC
57 enum {
58 PROFILE_CALLER_NAME,
59 PROFILE_CALLER_SELF,
60 PROFILE_CALLER_TOTAL,
61 PROFILE_CALLER_SYMBOL
64 enum {
65 PROFILE_DESCENDANTS_NAME,
66 PROFILE_DESCENDANTS_SELF,
67 PROFILE_DESCENDANTS_NONRECURSE,
68 PROFILE_DESCENDANTS_SYMBOL
71 enum {
72 LEAK_BLOCK_ADDR,
73 LEAK_BLOCK_SIZE,
74 LEAK_BLOCK_CALLER,
75 LEAK_BLOCK_BLOCK,
78 enum {
79 LEAK_STACK_NAME,
80 LEAK_STACK_LINE,
81 LEAK_STACK_FILE
84 char *glade_file;
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
108 static GtkWidget *
109 get_widget (GladeXML *glade,
110 const char *name)
112 GtkWidget *widget = glade_xml_get_widget (glade, name);
113 if (!widget)
114 g_error ("Cannot lookup %s\n", name);
116 return widget;
120 /************************************************************
121 * Status Page
122 ************************************************************/
124 static void
125 draw_bar (GtkWidget *widget, int red, int green, int blue,
126 int size)
128 GdkGC *gc = gdk_gc_new (widget->window);
129 GdkColor color;
131 color.red = red;
132 color.green = green;
133 color.blue = blue;
135 gdk_gc_set_rgb_fg_color (gc, &color);
137 gdk_draw_rectangle (widget->window, gc, TRUE,
138 0, 0, size, widget->allocation.height);
140 color.red = 0;
141 color.green = 0;
142 color.blue = 0;
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);
149 g_object_unref (gc);
152 static gboolean
153 on_usage_area_expose (GtkWidget *widget, GdkEvent *event,
154 gpointer data)
156 ProcessWindow *pwin = data;
157 int width;
158 int bytes_used;
159 int leak_size;
160 int high_size;
161 int current_size;
163 width = widget->allocation.width;
165 /* background */
166 draw_bar (widget, 0xffff, 0xffff, 0xffff, width);
168 /* bars */
169 if (pwin->process)
170 bytes_used = pwin->process->bytes_used;
171 else
172 bytes_used = 0;
174 /* bars */
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);
183 return TRUE;
186 static gboolean
187 update_status (gpointer data)
189 char *tmp;
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);
194 g_free (tmp);
196 tmp = g_strdup_printf ("%d", pwin->process->n_allocations);
197 gtk_label_set_text (GTK_LABEL (pwin->n_allocations_label), tmp);
198 g_free (tmp);
200 tmp = g_strdup_printf ("%d samples", pwin->process->bytes_used);
201 gtk_label_set_text (GTK_LABEL (pwin->profile_status_label), tmp);
202 g_free (tmp);
204 if (pwin->process->n_allocations == 0)
205 tmp = g_strdup("-");
206 else
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);
211 g_free (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);
219 g_free (tmp);
222 pwin->usage_high = MAX (pwin->process->bytes_used, pwin->usage_high);
224 gtk_widget_queue_draw (pwin->usage_area);
225 dw_update(pwin);
227 return TRUE;
231 /************************************************************
232 * GUI for profiles
233 ************************************************************/
235 static gboolean
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);
244 return FALSE;
247 static void
248 profile_func_list_goto_symbol (ProcessWindow *pwin, char *symbol)
250 GtkTreeModel *function_list;
251 GtkTreeIter iter;
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)) {
257 do {
258 ProfileFunc *func;
260 gtk_tree_model_get (function_list, &iter,
261 PROFILE_FUNC_FUNC, &func,
262 -1);
264 if (symbol_equal (func->node->symbol, symbol)) {
265 found = TRUE;
266 break;
268 } while (gtk_tree_model_iter_next (function_list, &iter));
271 if (found) {
272 GtkTreePath *path =
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));
281 static void
282 profile_descendants_row_activated (GtkTreeView *treeview,
283 GtkTreePath *path,
284 GtkTreeViewColumn *column,
285 gpointer data)
287 GtkTreeModel *descendants_store;
288 ProcessWindow *pwin = data;
289 GtkTreeIter iter;
290 char *desc_symbol;
292 descendants_store = gtk_tree_view_get_model (treeview);
293 if (!gtk_tree_model_get_iter (descendants_store, &iter, path))
294 return;
296 gtk_tree_model_get (descendants_store, &iter, PROFILE_DESCENDANTS_SYMBOL, &desc_symbol, -1);
298 profile_func_list_goto_symbol (pwin, desc_symbol);
301 static void
302 profile_caller_row_activated (GtkTreeView *treeview,
303 GtkTreePath *path,
304 GtkTreeViewColumn *column,
305 gpointer data)
307 GtkTreeModel *caller_list;
308 ProcessWindow *pwin = data;
309 GtkTreeIter iter;
310 char *caller_symbol;
312 caller_list = gtk_tree_view_get_model (treeview);
313 if (!gtk_tree_model_get_iter (caller_list, &iter, path))
314 return;
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);
322 static void
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);
327 else
328 gtk_list_store_set (GTK_LIST_STORE (model), iter, column, value, -1);
331 static void
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);
336 else
337 set_double (model, iter, column, 100 * (double)value / n_samples);
340 static void
341 add_node (GtkTreeStore *store, int n_samples,
342 const GtkTreeIter *parent, ProfileDescendantTreeNode *node)
344 GtkTreeIter iter;
345 gchar *name;
346 int i;
348 g_return_if_fail (GTK_IS_TREE_STORE (store));
350 gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
352 if (node->symbol)
353 name = elf_demangle (node->symbol);
354 else
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,
363 -1);
365 else {
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,
371 -1);
374 g_free (name);
376 for (i = 0; i < node->children->len; ++i)
377 add_node (store, n_samples, &iter, node->children->pdata[i]);
380 static void
381 profile_selection_changed (GtkTreeSelection *selection, ProcessWindow *pwin)
383 GtkListStore *store;
384 GtkTreeIter selected;
385 ProfileFunc *func;
386 GtkTreeStore *tree_store;
387 GtkListStore *list_store;
388 GtkTreeModel *list_model;
389 GtkTreePath *path;
390 GPtrArray *caller_list;
391 ProfileDescendantTree *descendant_tree;
392 int i;
393 int n_samples;
394 int old_sort_column;
395 GtkSortType old_sort_type;
396 gboolean was_sorted;
398 if (!gtk_tree_selection_get_selected (selection, (GtkTreeModel **)&store, &selected))
400 g_warning ("No selection");
401 return;
404 n_samples = pwin->profile->n_bytes;
405 gtk_tree_model_get (GTK_TREE_MODEL (store), &selected,
406 PROFILE_FUNC_FUNC, &func,
407 -1);
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,
414 G_TYPE_STRING,
415 G_TYPE_DOUBLE,
416 G_TYPE_DOUBLE,
417 G_TYPE_POINTER);
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
429 * descendants.
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);
435 if (was_sorted)
436 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
437 old_sort_column, old_sort_type);
438 else
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) {
462 GtkTreeIter iter;
463 gchar *name;
464 ProfileFunc *caller = caller_list->pdata[i];
466 if (caller->node) {
467 if (caller->node->symbol)
468 name = elf_demangle(caller->node->symbol);
469 else
470 name = g_strdup("???");
472 else
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),
480 -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);
485 g_free(name);
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),
492 list_model);
494 if (was_sorted)
495 gtk_tree_sortable_set_sort_column_id (
496 GTK_TREE_SORTABLE (list_store), old_sort_column, old_sort_type);
497 else
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);
510 static void
511 profile_fill (ProcessWindow *pwin)
513 GtkListStore *store;
514 GtkTreeModel *model;
516 int i;
517 int n_samples = pwin->profile->n_bytes;
519 int old_sort_column;
520 GtkSortType old_sort_type;
521 gboolean was_sorted;
523 was_sorted =
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) {
537 GtkTreeIter iter;
538 gchar *name;
540 ProfileFunc *func = pwin->profile->functions->pdata[i];
542 gtk_list_store_append (store, &iter);
544 g_assert (func);
546 if (func->node->symbol)
547 name = elf_demangle(func->node->symbol);
548 else
549 name = g_strdup("???");
551 gtk_list_store_set (store, &iter,
552 PROFILE_FUNC_NAME, name,
553 PROFILE_FUNC_FUNC, func,
554 -1);
556 set_sample (model, &iter, PROFILE_FUNC_SELF, func->self, n_samples);
557 set_sample (model, &iter, PROFILE_FUNC_TOTAL, func->total, n_samples);
559 g_free(name);
562 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_func_tree_view));
564 if (was_sorted) {
565 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), old_sort_column,
566 old_sort_type);
568 else {
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 ************************************************************/
587 static Block *
588 leak_block_get_selected (ProcessWindow *pwin)
590 GtkTreeSelection *selection;
591 GtkTreeModel *model;
592 GtkTreeIter iter;
593 Block *block = NULL;
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);
599 return block;
602 static void
603 leak_block_selection_changed (GtkTreeSelection *selection,
604 ProcessWindow *pwin)
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);
609 StackNode *stack;
611 gtk_list_store_clear (store);
613 if (block == NULL)
614 return;
616 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
618 for (stack = block->stack; stack != NULL; stack = stack->parent) {
619 GtkTreeIter iter;
620 const char *filename;
621 char *functionname;
622 unsigned int line;
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)";
629 line = 0;
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,
637 -1);
640 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
643 static void
644 leaks_fill (ProcessWindow *pwin)
646 GSList *tmp_list;
647 GtkTreeModel *model;
648 GtkListStore *store;
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;
658 while (tmp_list) {
659 const char *filename;
660 gboolean free_function = FALSE;
661 char *functionname = NULL;
662 GtkTreeIter iter;
664 unsigned int line;
666 Block *block = tmp_list->data;
667 StackNode *stack;
669 for (stack = block->stack; stack != NULL; stack = stack->parent) {
670 if (process_find_line (pwin->process, stack->address,
671 &filename, &functionname, &line)) {
672 GSList *tmp_list;
674 if (!functionname)
675 continue;
677 for (tmp_list = skip_funcs; tmp_list != NULL; tmp_list = tmp_list->next) {
678 if (!strcmp (functionname, tmp_list->data)) {
679 functionname = NULL;
680 break;
684 if (!functionname)
685 continue;
687 for (tmp_list = skip_regexes; tmp_list != NULL; tmp_list = tmp_list->next) {
688 regex_t regex;
690 regcomp (&regex, tmp_list->data, 0);
692 if (!regexec (&regex, functionname, 0, NULL, 0)) {
693 functionname = NULL;
694 regfree (&regex);
695 break;
698 regfree (&regex);
702 if (functionname)
703 break;
705 if (!functionname) {
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,
716 -1);
718 if (free_function)
719 g_free (functionname);
721 tmp_list = tmp_list->next;
724 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->leak_block_tree_view));
727 static void
728 leak_stack_run_command (ProcessWindow *pwin, Block *block, int frame)
730 const char *filename;
731 char *functionname;
732 unsigned int line;
734 StackNode *stack = block->stack;
735 while (frame--)
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;
743 char buf[32];
744 char *cmdline;
745 GError *err = NULL;
747 while (*p) {
748 if (*p == '%') {
749 switch (*++p) {
750 case 'f':
751 g_string_append (command, filename);
752 break;
753 case 'l':
754 snprintf(buf, 32, "%d", line);
755 g_string_append (command, buf);
756 break;
757 case '%':
758 g_string_append_c (command, '%');
759 break;
760 default:
761 g_string_append_c (command, '%');
762 g_string_append_c (command, *p);
764 } else
765 g_string_append_c (command, *p);
766 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);
776 g_error_free (err);
779 g_free (cmdline);
781 g_string_free (command, FALSE);
785 static void
786 leak_stack_row_activated (GtkTreeView *tree_view,
787 GtkTreePath *path,
788 int column,
789 ProcessWindow *pwin)
791 GtkTreeModel *model;
792 GtkTreeIter iter;
793 int frame;
794 Block *block;
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);
801 if (block)
802 leak_stack_run_command (pwin, block, frame);
807 /************************************************************
808 * File Selection handling
809 ************************************************************/
811 static gchar *
812 get_filename (const gchar *title,
813 const gchar *suggested_name)
815 GtkWidget *dialog;
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,
821 NULL);
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);
827 return filename;
830 /* Really ugly utility function to retrieve the ProcessWindow from
831 * either a menu_item or toolbar item.
833 ProcessWindow *
834 pwin_from_widget (GtkWidget *widget)
836 GtkWidget *app;
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);
847 } else
848 app = gtk_widget_get_toplevel (widget);
850 return g_object_get_data (G_OBJECT (app), "process-window");
853 void
854 close_cb (GtkWidget *widget)
856 ProcessWindow *pwin = pwin_from_widget (widget);
858 hide_and_check_quit (pwin->main_window);
861 void
862 exit_cb (GtkWidget *widget)
864 gtk_main_quit ();
867 static void
868 reset_cb (MPProcess *process, ProcessWindow *pwin)
870 process_window_reset (pwin);
873 static void
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);
882 } else {
883 tree_window_remove (pwin);
885 if (pwin->process) {
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);
900 } else {
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);
906 g_free (title);
907 g_free (status);
908 g_free (cmdline);
912 static void
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));
919 static void
920 process_window_reset (ProcessWindow *pwin)
922 if (pwin->profile) {
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);
935 if (pwin->leaks) {
936 g_slist_free (pwin->leaks);
937 pwin->leaks = NULL;
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);
949 dw_update(pwin);
952 static void
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 =
962 g_timeout_add (100,
963 update_status,
964 pwin);
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);
974 static void
975 process_created_cb (MPServer *server, MPProcess *process)
977 ProcessWindow *pwin = process_window_new ();
979 init_process (pwin, process);
981 tree_window_show ();
984 static gboolean
985 run_file (ProcessWindow *pwin, char **args)
987 gboolean result;
988 char *path;
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);
995 if (path) {
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);
1009 result = TRUE;
1011 } else {
1012 show_error (pwin->main_window,
1013 ERROR_MODAL,
1014 _("Cannot find executable for \"%s\""),
1015 args[0]);
1016 result = FALSE;
1019 g_free (path);
1020 return result;
1024 void
1025 run_cb (GtkWidget *widget)
1027 GladeXML *xml;
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));
1039 while (1) {
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) {
1043 gchar **args;
1044 char *text;
1045 gboolean result;
1047 text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
1048 if (!text)
1049 break;
1051 args = process_parse_exec (text);
1053 result = run_file (pwin, args);
1055 g_strfreev (args);
1056 g_free (text);
1058 if (result)
1059 break;
1060 } else {
1061 break;
1065 gtk_widget_destroy (run_dialog);
1068 void
1069 kill_cb (GtkWidget *widget)
1071 ProcessWindow *pwin = pwin_from_widget (widget);
1073 if (pwin->process)
1074 process_window_maybe_kill (pwin);
1077 void
1078 detach_cb (GtkWidget *widget)
1080 ProcessWindow *pwin = pwin_from_widget (widget);
1082 if (pwin->process)
1083 process_window_maybe_detach (pwin);
1086 void
1087 process_tree_cb (GtkWidget *widget)
1089 tree_window_show ();
1092 void
1093 save_leak_cb (GtkWidget *widget)
1095 static gchar *suggestion = NULL;
1096 gchar *filename;
1098 ProcessWindow *pwin = pwin_from_widget (widget);
1100 if (pwin->leaks) {
1101 filename = get_filename ("Save Leak Report",
1102 suggestion ? suggestion : "memprof.leak");
1103 if (filename) {
1104 g_free (suggestion);
1105 suggestion = filename;
1107 leaks_print (pwin->process, pwin->leaks, filename);
1108 g_free (filename);
1113 void
1114 save_profile_cb (GtkWidget *widget)
1116 static gchar *suggestion = NULL;
1117 gchar *filename;
1119 ProcessWindow *pwin = pwin_from_widget (widget);
1121 if (pwin->profile) {
1122 filename = get_filename ("Save Profile",
1123 suggestion ? suggestion : "memprof.out");
1124 if (filename) {
1125 g_free (suggestion);
1126 suggestion = filename;
1128 profile_write (pwin->profile, filename);
1129 g_free (filename);
1134 void
1135 save_current_cb (GtkWidget *widget)
1137 ProcessWindow *pwin = pwin_from_widget (widget);
1139 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin->main_notebook))) {
1140 case 0:
1141 save_profile_cb (widget);
1142 break;
1143 case 1:
1144 save_leak_cb (widget);
1145 break;
1149 void
1150 generate_leak_cb (GtkWidget *widget)
1152 GSList *tmp_list;
1153 ProcessWindow *pwin = pwin_from_widget (widget);
1156 if (pwin->process) {
1157 process_stop_input (pwin->process);
1159 while (pwin->leaks) {
1160 block_unref(pwin->leaks->data);
1161 pwin->leaks = g_slist_delete_link(pwin->leaks, pwin->leaks);
1163 pwin->leaks = leaks_find (pwin->process);
1165 pwin->usage_leaked = 0;
1166 tmp_list = pwin->leaks;
1167 while (tmp_list) {
1168 pwin->usage_leaked += ((Block *)tmp_list->data)->size;
1169 tmp_list = tmp_list->next;
1172 leaks_fill (pwin);
1173 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 1);
1175 process_start_input (pwin->process);
1179 void
1180 generate_profile_cb (GtkWidget *widget)
1182 ProcessWindow *pwin = pwin_from_widget (widget);
1184 if (pwin->process) {
1185 process_stop_input (pwin->process);
1187 if (pwin->profile) {
1188 profile_free (pwin->profile);
1189 pwin->profile = NULL;
1192 pwin->profile = profile_create (pwin->process, skip_funcs);
1193 process_start_input (pwin->process);
1194 profile_fill (pwin);
1196 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 0);
1200 void
1201 draw_memmap_toggled_cb (GtkWidget *widget)
1203 ProcessWindow *pwin = pwin_from_widget (widget);
1204 pwin->draw_memmap = gtk_toggle_tool_button_get_active
1205 (GTK_TOGGLE_TOOL_BUTTON (widget));
1206 dw_update(pwin);
1209 void
1210 reset_profile_cb (GtkWidget *widget)
1212 ProcessWindow *pwin = pwin_from_widget (widget);
1214 process_window_reset (pwin);
1216 if (pwin->process)
1217 process_clear_input (pwin->process);
1220 void
1221 record_button_toggled_cb (GtkWidget *widget)
1223 ProcessWindow *pwin = pwin_from_widget (widget);
1225 if (gtk_toggle_tool_button_get_active
1226 (GTK_TOGGLE_TOOL_BUTTON (widget)))
1227 process_start_input (pwin->process);
1228 else
1229 process_stop_input (pwin->process);
1232 void
1233 about_cb (GtkWidget *widget)
1235 #define OSLASH "\303\270"
1236 ProcessWindow *pwin = pwin_from_widget (widget);
1238 /* FIXME: restore credits */
1239 gtk_show_about_dialog (GTK_WINDOW (pwin->main_window),
1240 #if 0
1241 "logo", pwin->icon,
1242 #endif
1243 "name", "MemProf",
1244 "copyright",
1245 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1246 "Copyright 2002, Kristian Rietveld\n"
1247 "Copyright 2002, 2006, 2007, S"OSLASH"ren Sandmann",
1248 #if 0
1249 "copyright", "Copyright 2004-2006, S"OSLASH"ren Sandmann",
1250 #endif
1251 "version", PACKAGE_VERSION,
1252 NULL);
1255 static void
1256 show_error_response (GtkDialog *dialog,
1257 gint response_id,
1258 gpointer user_data)
1260 if (response_id == GTK_RESPONSE_OK)
1261 gtk_widget_destroy (GTK_WIDGET (dialog));
1264 void
1265 show_error (GtkWidget *parent_window,
1266 ErrorType error,
1267 const gchar *format,
1268 ...)
1270 va_list args;
1271 char *message;
1272 GtkWidget *dialog;
1274 va_start (args, format);
1275 g_vasprintf (&message, format, args);
1276 va_end (args);
1278 dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL,
1279 GTK_DIALOG_DESTROY_WITH_PARENT,
1280 (error == ERROR_FATAL) ?
1281 GTK_MESSAGE_ERROR :
1282 GTK_MESSAGE_WARNING,
1283 GTK_BUTTONS_OK, "%s", message);
1284 g_free (message);
1286 gtk_window_set_title (GTK_WINDOW (dialog),
1287 (error == ERROR_FATAL) ?
1288 _("MemProf Error") : _("MemProf Warning"));
1290 if (error == ERROR_WARNING) {
1291 gtk_widget_show (dialog);
1292 g_signal_connect (dialog, "response",
1293 G_CALLBACK (show_error_response), NULL);
1294 } else {
1295 gtk_dialog_run (GTK_DIALOG (dialog));
1296 gtk_widget_destroy (dialog);
1299 if (error == ERROR_FATAL)
1300 exit(1);
1303 static void
1304 process_window_free (ProcessWindow *pwin)
1306 /* FIXME: we leak the process structure */
1308 if (pwin->leaks)
1309 g_slist_free (pwin->leaks);
1311 if (pwin->profile)
1312 profile_free (pwin->profile);
1314 process_windows = g_slist_remove (process_windows, pwin);
1315 if (!process_windows)
1316 gtk_main_quit ();
1318 g_free (pwin);
1322 static void
1323 process_window_destroy (ProcessWindow *pwin)
1325 if (pwin->status_update_timeout)
1326 g_source_remove (pwin->status_update_timeout);
1328 gtk_widget_destroy (pwin->main_window);
1329 check_quit ();
1332 static GtkTreeViewColumn *
1333 add_sample_column (GtkTreeView *view, const gchar *title, gint model_column)
1335 const char *format;
1337 if (profile_type == MP_PROFILE_MEMORY)
1338 format = "%.0f";
1339 else
1340 format = "%.2f";
1342 return add_double_format_column (view, title, model_column, format);
1345 static void
1346 setup_profile_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1348 GtkTreeSelection *selection;
1349 GtkTreeViewColumn *col;
1351 col = add_plain_text_column (tree_view, _("Functions"), PROFILE_FUNC_NAME);
1352 add_sample_column (tree_view, _("Self"), PROFILE_FUNC_SELF);
1353 add_sample_column (tree_view, _("Total"), PROFILE_FUNC_TOTAL);
1354 gtk_tree_view_column_set_expand (col, TRUE);
1356 selection = gtk_tree_view_get_selection (tree_view);
1357 g_return_if_fail (selection != NULL);
1358 g_signal_connect (selection, "changed", G_CALLBACK (profile_selection_changed), pwin);
1360 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1363 static void
1364 setup_profile_descendants_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1366 GtkTreeViewColumn *col;
1368 col = add_plain_text_column (tree_view, _("Descendants"), PROFILE_DESCENDANTS_NAME);
1369 add_sample_column (tree_view, _("Self"), PROFILE_DESCENDANTS_SELF);
1370 add_sample_column (tree_view, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE);
1371 gtk_tree_view_column_set_expand (col, TRUE);
1373 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), FALSE);
1375 g_signal_connect (tree_view, "row-activated",
1376 G_CALLBACK (profile_descendants_row_activated), pwin);
1378 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1381 static void
1382 setup_profile_caller_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1384 GtkTreeViewColumn *col;
1386 col = add_plain_text_column (tree_view, _("Callers"), PROFILE_CALLER_NAME);
1387 add_sample_column (tree_view, _("Self"), PROFILE_CALLER_SELF);
1388 add_sample_column (tree_view, _("Total"), PROFILE_CALLER_TOTAL);
1389 gtk_tree_view_column_set_expand (col, TRUE);
1391 g_signal_connect (tree_view, "row-activated",
1392 G_CALLBACK (profile_caller_row_activated), pwin);
1394 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1397 static void
1398 setup_leak_block_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1400 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
1401 GtkListStore *store;
1403 g_return_if_fail (selection != NULL);
1405 store = gtk_list_store_new (4,
1406 G_TYPE_POINTER,
1407 G_TYPE_INT,
1408 G_TYPE_STRING,
1409 G_TYPE_POINTER);
1411 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
1413 add_pointer_column (tree_view, _("Address"), LEAK_BLOCK_ADDR);
1414 add_plain_text_column (tree_view, _("Size"), LEAK_BLOCK_SIZE);
1415 add_plain_text_column (tree_view, _("Caller"), LEAK_BLOCK_CALLER);
1417 g_signal_connect (selection, "changed",
1418 G_CALLBACK (leak_block_selection_changed), pwin);
1420 gtk_tree_view_columns_autosize (tree_view);
1423 static void
1424 setup_leak_stack_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1426 GtkListStore *store;
1428 store = gtk_list_store_new (3,
1429 G_TYPE_STRING,
1430 G_TYPE_INT,
1431 G_TYPE_STRING);
1433 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
1435 add_plain_text_column (tree_view, _("Function"), LEAK_STACK_NAME);
1436 add_plain_text_column (tree_view, _("Line"), LEAK_STACK_LINE);
1437 add_plain_text_column (tree_view, _("File"), LEAK_STACK_FILE);
1439 g_signal_connect (tree_view, "row-activated",
1440 G_CALLBACK (leak_stack_row_activated), pwin);
1442 gtk_tree_view_columns_autosize (tree_view);
1445 static void
1446 set_default_size (GtkWindow *window)
1448 GdkScreen *screen;
1449 int monitor_num;
1450 GdkRectangle monitor;
1451 int width, height;
1452 GtkWidget *widget = GTK_WIDGET (window);
1454 screen = gtk_widget_get_screen (widget);
1455 monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
1457 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1459 width = monitor.width * 3 / 4;
1460 height = monitor.height * 3 / 4;
1462 gtk_window_resize (window, width, height);
1465 static ProcessWindow *
1466 process_window_new (void)
1468 gchar *fullfilename;
1469 GladeXML *xml;
1470 GtkWidget *vpaned;
1471 GtkWidget *hpaned;
1472 ProcessWindow *pwin;
1473 GError *err = NULL;
1475 pwin = g_new0 (ProcessWindow, 1);
1476 process_windows = g_slist_prepend (process_windows, pwin);
1478 pwin->process = NULL;
1479 pwin->profile = NULL;
1480 pwin->leaks = NULL;
1482 pwin->usage_max = 32*1024;
1483 pwin->usage_high = 0;
1484 pwin->usage_leaked = 0;
1486 xml = glade_xml_new (glade_file, "MainWindow", NULL);
1488 pwin->main_window = get_widget (xml, "MainWindow");
1489 gtk_widget_realize (pwin->main_window);
1491 fullfilename = g_strdup ("./memprof.png");
1493 if (!g_file_test (fullfilename, G_FILE_TEST_EXISTS))
1494 fullfilename = g_build_filename (DATADIR, "memprof.png", NULL);
1497 gtk_window_set_icon_from_file (GTK_WINDOW (pwin->main_window), fullfilename, &err);
1499 g_free (fullfilename);
1501 g_signal_connect (pwin->main_window, "delete_event",
1502 G_CALLBACK (hide_and_check_quit), pwin);
1505 set_default_size (GTK_WINDOW (pwin->main_window));
1507 g_object_set_data_full (G_OBJECT (pwin->main_window),
1508 "process-window",
1509 pwin, (GDestroyNotify)process_window_free);
1511 pwin->main_notebook = get_widget (xml, "main-notebook");
1513 pwin->n_allocations_label = get_widget (xml, "n-allocations-label");
1514 pwin->bytes_per_label = get_widget (xml, "bytes-per-label");
1515 pwin->total_bytes_label = get_widget (xml, "total-bytes-label");
1517 pwin->profile_status_label = get_widget (xml, "profile-status-label");
1519 /* setup profile tree views */
1520 pwin->profile_func_tree_view = get_widget (xml, "profile-func-tree-view");
1521 pwin->profile_descendants_tree_view =
1522 get_widget (xml, "profile-descendants-tree-view");
1523 pwin->profile_caller_tree_view = get_widget (xml, "profile-caller-tree-view");
1525 setup_profile_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_func_tree_view));
1526 setup_profile_descendants_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_descendants_tree_view));
1527 setup_profile_caller_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_caller_tree_view));
1529 /* leak tree views */
1530 pwin->leak_block_tree_view = get_widget (xml, "leak-block-tree-view");
1531 pwin->leak_stack_tree_view = get_widget (xml, "leak-stack-tree-view");
1533 setup_leak_block_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_block_tree_view));
1534 setup_leak_stack_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_stack_tree_view));
1536 pwin->usage_max_label = get_widget (xml, "usage-max-label");
1537 pwin->usage_area = get_widget (xml, "usage-area");
1539 g_signal_connect (pwin->usage_area, "expose_event",
1540 G_CALLBACK (on_usage_area_expose), pwin);
1542 vpaned = get_widget (xml, "profile-vpaned");
1543 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1545 hpaned = get_widget (xml, "profile-hpaned");
1546 gtk_paned_set_position (GTK_PANED (hpaned), 150);
1548 vpaned = get_widget (xml, "leaks-vpaned");
1549 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1551 /* If profiling time, not memory, hide all GUI related to leak
1552 * detection.
1554 if (profile_type != MP_PROFILE_MEMORY) {
1555 gtk_widget_hide (get_widget (xml, "leaks-vpaned"));
1556 gtk_widget_hide (get_widget (xml, "toolbar-leaks-button"));
1557 gtk_widget_hide (get_widget (xml, "save-leak-info"));
1558 gtk_widget_hide (get_widget (xml, "generate-leak-report"));
1559 gtk_widget_hide (get_widget (xml, "allocation-bar"));
1560 gtk_notebook_set_show_tabs (
1561 GTK_NOTEBOOK (get_widget (xml, "main-notebook")), FALSE);
1563 else {
1564 gtk_widget_hide (get_widget (xml, "profile-status-label"));
1565 gtk_widget_hide (get_widget (xml, "reset-profile-button"));
1568 pwin->time_graph = get_widget(xml, "time-graph");
1569 pwin->mem_map = get_widget(xml, "mem-map");
1570 g_signal_connect(pwin->time_graph, "expose_event",
1571 G_CALLBACK (time_graph_expose_event), pwin);
1572 g_signal_connect(pwin->mem_map, "expose_event",
1573 G_CALLBACK (mem_map_expose_event), pwin);
1575 glade_xml_signal_autoconnect (xml);
1576 g_object_unref (G_OBJECT (xml));
1578 return pwin;
1582 MPProcess *
1583 process_window_get_process (ProcessWindow *pwin)
1585 return pwin->process;
1588 gboolean
1589 process_window_visible (ProcessWindow *pwin)
1591 return GTK_WIDGET_VISIBLE (pwin->main_window);
1594 void
1595 process_window_show (ProcessWindow *pwin)
1597 if (!process_window_visible (pwin))
1598 gtk_widget_show (pwin->main_window);
1599 else
1600 gdk_window_show (pwin->main_window->window);
1603 void
1604 process_window_hide (ProcessWindow *pwin)
1606 if (process_window_visible (pwin))
1607 hide_and_check_quit (pwin->main_window);
1610 void
1611 check_quit (void)
1613 GList *toplevels, *tmplist;
1615 tmplist = toplevels = gtk_window_list_toplevels ();
1616 while (tmplist) {
1617 if (GTK_WIDGET_VISIBLE (toplevels->data))
1618 return;
1619 tmplist = tmplist->next;
1622 g_list_free (toplevels);
1624 gtk_main_quit ();
1627 gboolean
1628 hide_and_check_quit (GtkWidget *window)
1630 gtk_widget_hide (window);
1631 check_quit ();
1633 return TRUE;
1637 void
1638 process_window_maybe_detach (ProcessWindow *pwin)
1640 GtkWidget *dialog;
1641 const char *message;
1642 gint response;
1644 if (pwin->process->status == MP_PROCESS_EXITING)
1645 message = _("Really detach from finished process?");
1646 else
1647 message = _("Really detach from running process?");
1649 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1650 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1651 GTK_MESSAGE_QUESTION,
1652 GTK_BUTTONS_YES_NO,
1653 "%s", message);
1654 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1656 response = gtk_dialog_run (GTK_DIALOG (dialog));
1657 gtk_widget_destroy (dialog);
1659 if (response == GTK_RESPONSE_YES)
1660 process_detach (pwin->process);
1664 void
1665 process_window_maybe_kill (ProcessWindow *pwin)
1667 if (pwin->process->status == MP_PROCESS_EXITING)
1668 process_window_maybe_detach (pwin);
1669 else {
1670 GtkWidget *dialog;
1671 gint response;
1673 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1674 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1675 GTK_MESSAGE_QUESTION,
1676 GTK_BUTTONS_YES_NO,
1677 _("Really kill running process?"));
1678 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1679 GTK_RESPONSE_YES);
1681 response = gtk_dialog_run (GTK_DIALOG (dialog));
1682 gtk_widget_destroy (dialog);
1684 if (response == GTK_RESPONSE_YES)
1685 process_kill (pwin->process);
1689 void
1690 sigchld_handler (int signum)
1692 int old_errno = errno;
1694 while (1) {
1695 int pid = waitpid (WAIT_ANY, NULL, WNOHANG);
1696 if (pid < 0 && errno != ECHILD)
1697 g_error ("waitpid: %s\n", g_strerror (errno));
1698 else if (pid <= 0)
1699 break;
1702 errno = old_errno;
1705 static char *profile_type_string = NULL;
1706 static char *profile_rate_string = NULL;
1707 static int profile_interval = 1000;
1708 static gchar **profile_skip_funcs = NULL;
1710 static const GOptionEntry entries[] =
1712 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE, &default_follow_fork,
1713 N_("Create new windows for forked processes"), NULL },
1714 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE, &default_follow_exec,
1715 N_("Retain windows for processes after exec()"), NULL },
1716 { "profile", '\0', 0, G_OPTION_ARG_STRING, &profile_type_string,
1717 N_("Type of profiling information to collect"), "memory/cycles/time" },
1718 { "rate", '\0', 0, G_OPTION_ARG_STRING, &profile_rate_string,
1719 N_("Number of samples/sec for time profile (1k=1000)"), NULL },
1720 { "skip-funcs", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &profile_skip_funcs,
1721 N_("Functions allocating memory"), "function_name" },
1722 { NULL }
1725 static void
1726 parse_options (int *argc, char ***argv)
1728 GError *err = NULL;
1729 GOptionContext *context;
1731 context = g_option_context_new ("- A memory profiler");
1733 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
1734 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1735 g_option_context_parse (context, argc, argv, &err);
1738 static void
1739 initialize_skip_funcs ()
1741 gint i = 0;
1743 skip_funcs = g_slist_append (skip_funcs, "g_malloc");
1744 skip_funcs = g_slist_append (skip_funcs, "g_malloc0");
1745 skip_funcs = g_slist_append (skip_funcs, "g_realloc");
1746 skip_funcs = g_slist_append (skip_funcs, "g_strdup");
1747 skip_funcs = g_slist_append (skip_funcs, "g_strndup");
1748 skip_funcs = g_slist_append (skip_funcs, "g_slice_alloc");
1749 skip_funcs = g_slist_append (skip_funcs, "g_slice_alloc0");
1750 skip_funcs = g_slist_append (skip_funcs, "strdup");
1751 skip_funcs = g_slist_append (skip_funcs, "strndup");
1752 skip_funcs = g_slist_append (skip_funcs, "_Znwj");
1753 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF16fastZeroedMallocEj");
1754 skip_funcs = g_slist_append (skip_funcs, "_Z7qMallocj");
1755 skip_funcs = g_slist_append (skip_funcs, "_Z8qReallocPvj");
1756 skip_funcs = g_slist_append (skip_funcs, "_hb_alloc");
1758 while (profile_skip_funcs && profile_skip_funcs [i]) {
1759 skip_funcs = g_slist_append (skip_funcs, profile_skip_funcs [i]);
1760 ++i;
1765 main(int argc, char **argv)
1767 ProcessWindow *initial_window;
1769 gtk_init (&argc, &argv);
1771 parse_options (&argc, &argv);
1773 /* Set up a handler for SIGCHLD to avoid zombie children
1775 signal (SIGCHLD, sigchld_handler);
1777 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1778 textdomain (GETTEXT_PACKAGE);
1780 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1781 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1782 #endif
1784 /* If the user didn't specify the profile type explicitely,
1785 * we guess from the executable name.
1787 if (!profile_type_string) {
1788 char *basename;
1789 basename = g_path_get_basename (argv[0]);
1791 if (strcmp (basename, "speedprof") == 0)
1792 profile_type_string = "cycles";
1793 else
1794 profile_type_string = "memory";
1796 g_free (basename);
1799 if (strcmp (profile_type_string, "memory") == 0)
1800 profile_type = MP_PROFILE_MEMORY;
1801 else if (strcmp (profile_type_string, "cycles") == 0)
1802 profile_type = MP_PROFILE_CYCLES;
1803 else if (strcmp (profile_type_string, "time") == 0)
1804 profile_type = MP_PROFILE_TIME;
1805 else {
1806 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1807 exit (1);
1810 initialize_skip_funcs();
1812 if (profile_rate_string) {
1813 int multiplier = 1;
1814 double rate;
1815 int len = strlen (profile_rate_string);
1816 char suffix[2] = { '\0', '\0' };
1817 char *end;
1819 if (len > 0 &&
1820 (profile_rate_string[len - 1] == 'k' ||
1821 profile_rate_string[len - 1] == 'K')) {
1822 suffix[0] = profile_rate_string[len - 1];
1823 multiplier = 1000;
1824 profile_rate_string[len - 1] = '\0';
1827 rate = strtod (profile_rate_string, &end);
1828 if (len == 0 || *end != '\0' ||
1829 rate * multiplier <= 1 || rate * multiplier > 1000000) {
1830 g_printerr ("Invalid rate: %s%s\n",
1831 profile_rate_string, suffix);
1832 exit (1);
1835 profile_interval = (int) (0.5 + 1000000 / (rate * multiplier));
1838 glade_file = "./memprof.glade";
1839 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1840 glade_file = g_build_filename (DATADIR, "memprof.glade", NULL);
1842 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1843 show_error (NULL, ERROR_FATAL, _("Cannot find memprof.glade"));
1846 global_server = mp_server_new ();
1847 mp_server_set_profile_type (global_server, profile_type);
1848 mp_server_set_interval (global_server, profile_interval);
1850 g_signal_connect (global_server, "process_created",
1851 G_CALLBACK (process_created_cb), NULL);
1853 initial_window = process_window_new ();
1855 gtk_widget_show (initial_window->main_window);
1857 if (argc > 1)
1858 run_file (initial_window, (char **)(argv + 1));
1860 gtk_main ();
1862 return 0;