src/main.c: Make it possible to pass options to the app under test
[memprof.git] / src / main.c
blob77c2bc2946eac5201e5f14a841b4c55ab7045fd1
1 /* -*- mode: C; c-file-style: "linux" -*- */
3 /* MemProf -- memory profiler and leak detector
4 * Copyright 1999, 2000, 2001, Red Hat, Inc.
5 * Copyright 2002, Kristian Rietveld
6 * Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk)
7 * Copyright 2009, 2010, Holger Hans Peter Fryther
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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 if (suggestion)
1105 g_free (suggestion);
1106 suggestion = filename;
1108 leaks_print (pwin->process, pwin->leaks, filename);
1109 g_free (filename);
1114 void
1115 save_profile_cb (GtkWidget *widget)
1117 static gchar *suggestion = NULL;
1118 gchar *filename;
1120 ProcessWindow *pwin = pwin_from_widget (widget);
1122 if (pwin->profile) {
1123 filename = get_filename ("Save Profile",
1124 suggestion ? suggestion : "memprof.out");
1125 if (filename) {
1126 if (suggestion)
1127 g_free (suggestion);
1128 suggestion = filename;
1130 profile_write (pwin->profile, filename);
1131 g_free (filename);
1136 void
1137 save_current_cb (GtkWidget *widget)
1139 ProcessWindow *pwin = pwin_from_widget (widget);
1141 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin->main_notebook))) {
1142 case 0:
1143 save_profile_cb (widget);
1144 break;
1145 case 1:
1146 save_leak_cb (widget);
1147 break;
1151 void
1152 generate_leak_cb (GtkWidget *widget)
1154 GSList *tmp_list;
1155 ProcessWindow *pwin = pwin_from_widget (widget);
1158 if (pwin->process) {
1159 process_stop_input (pwin->process);
1161 while (pwin->leaks) {
1162 block_unref(pwin->leaks->data);
1163 pwin->leaks = g_slist_delete_link(pwin->leaks, pwin->leaks);
1165 pwin->leaks = leaks_find (pwin->process);
1167 pwin->usage_leaked = 0;
1168 tmp_list = pwin->leaks;
1169 while (tmp_list) {
1170 pwin->usage_leaked += ((Block *)tmp_list->data)->size;
1171 tmp_list = tmp_list->next;
1174 leaks_fill (pwin);
1175 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 1);
1177 process_start_input (pwin->process);
1181 void
1182 generate_profile_cb (GtkWidget *widget)
1184 ProcessWindow *pwin = pwin_from_widget (widget);
1186 if (pwin->process) {
1187 process_stop_input (pwin->process);
1189 if (pwin->profile) {
1190 profile_free (pwin->profile);
1191 pwin->profile = NULL;
1194 pwin->profile = profile_create (pwin->process, skip_funcs);
1195 process_start_input (pwin->process);
1196 profile_fill (pwin);
1198 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 0);
1202 void
1203 draw_memmap_toggled_cb (GtkWidget *widget)
1205 ProcessWindow *pwin = pwin_from_widget (widget);
1206 pwin->draw_memmap = gtk_toggle_tool_button_get_active
1207 (GTK_TOGGLE_TOOL_BUTTON (widget));
1208 dw_update(pwin);
1211 void
1212 reset_profile_cb (GtkWidget *widget)
1214 ProcessWindow *pwin = pwin_from_widget (widget);
1216 process_window_reset (pwin);
1218 if (pwin->process)
1219 process_clear_input (pwin->process);
1222 void
1223 record_button_toggled_cb (GtkWidget *widget)
1225 ProcessWindow *pwin = pwin_from_widget (widget);
1227 if (gtk_toggle_tool_button_get_active
1228 (GTK_TOGGLE_TOOL_BUTTON (widget)))
1229 process_start_input (pwin->process);
1230 else
1231 process_stop_input (pwin->process);
1234 void
1235 about_cb (GtkWidget *widget)
1237 #define OSLASH "\303\270"
1238 ProcessWindow *pwin = pwin_from_widget (widget);
1240 /* FIXME: restore credits */
1241 gtk_show_about_dialog (GTK_WINDOW (pwin->main_window),
1242 #if 0
1243 "logo", pwin->icon,
1244 #endif
1245 "name", "MemProf",
1246 "copyright",
1247 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1248 "Copyright 2002, Kristian Rietveld\n"
1249 "Copyright 2002, 2006, 2007, S"OSLASH"ren Sandmann",
1250 #if 0
1251 "copyright", "Copyright 2004-2006, S"OSLASH"ren Sandmann",
1252 #endif
1253 "version", PACKAGE_VERSION,
1254 NULL);
1257 static void
1258 show_error_response (GtkDialog *dialog,
1259 gint response_id,
1260 gpointer user_data)
1262 if (response_id == GTK_RESPONSE_OK)
1263 gtk_widget_destroy (GTK_WIDGET (dialog));
1266 void
1267 show_error (GtkWidget *parent_window,
1268 ErrorType error,
1269 const gchar *format,
1270 ...)
1272 va_list args;
1273 char *message;
1274 GtkWidget *dialog;
1276 va_start (args, format);
1277 g_vasprintf (&message, format, args);
1278 va_end (args);
1280 dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL,
1281 GTK_DIALOG_DESTROY_WITH_PARENT,
1282 (error == ERROR_FATAL) ?
1283 GTK_MESSAGE_ERROR :
1284 GTK_MESSAGE_WARNING,
1285 GTK_BUTTONS_OK, "%s", message);
1286 g_free (message);
1288 gtk_window_set_title (GTK_WINDOW (dialog),
1289 (error == ERROR_FATAL) ?
1290 _("MemProf Error") : _("MemProf Warning"));
1292 if (error == ERROR_WARNING) {
1293 gtk_widget_show (dialog);
1294 g_signal_connect (dialog, "response",
1295 G_CALLBACK (show_error_response), NULL);
1296 } else {
1297 gtk_dialog_run (GTK_DIALOG (dialog));
1298 gtk_widget_destroy (dialog);
1301 if (error == ERROR_FATAL)
1302 exit(1);
1305 static void
1306 process_window_free (ProcessWindow *pwin)
1308 /* FIXME: we leak the process structure */
1310 if (pwin->leaks)
1311 g_slist_free (pwin->leaks);
1313 if (pwin->profile)
1314 profile_free (pwin->profile);
1316 process_windows = g_slist_remove (process_windows, pwin);
1317 if (!process_windows)
1318 gtk_main_quit ();
1320 g_free (pwin);
1324 static void
1325 process_window_destroy (ProcessWindow *pwin)
1327 if (pwin->status_update_timeout)
1328 g_source_remove (pwin->status_update_timeout);
1330 gtk_widget_destroy (pwin->main_window);
1331 check_quit ();
1334 static GtkTreeViewColumn *
1335 add_sample_column (GtkTreeView *view, const gchar *title, gint model_column)
1337 const char *format;
1339 if (profile_type == MP_PROFILE_MEMORY)
1340 format = "%.0f";
1341 else
1342 format = "%.2f";
1344 return add_double_format_column (view, title, model_column, format);
1347 static void
1348 setup_profile_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1350 GtkTreeSelection *selection;
1351 GtkTreeViewColumn *col;
1353 col = add_plain_text_column (tree_view, _("Functions"), PROFILE_FUNC_NAME);
1354 add_sample_column (tree_view, _("Self"), PROFILE_FUNC_SELF);
1355 add_sample_column (tree_view, _("Total"), PROFILE_FUNC_TOTAL);
1356 gtk_tree_view_column_set_expand (col, TRUE);
1358 selection = gtk_tree_view_get_selection (tree_view);
1359 g_return_if_fail (selection != NULL);
1360 g_signal_connect (selection, "changed", G_CALLBACK (profile_selection_changed), pwin);
1362 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1365 static void
1366 setup_profile_descendants_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1368 GtkTreeViewColumn *col;
1370 col = add_plain_text_column (tree_view, _("Descendants"), PROFILE_DESCENDANTS_NAME);
1371 add_sample_column (tree_view, _("Self"), PROFILE_DESCENDANTS_SELF);
1372 add_sample_column (tree_view, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE);
1373 gtk_tree_view_column_set_expand (col, TRUE);
1375 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), FALSE);
1377 g_signal_connect (tree_view, "row-activated",
1378 G_CALLBACK (profile_descendants_row_activated), pwin);
1380 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1383 static void
1384 setup_profile_caller_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1386 GtkTreeViewColumn *col;
1388 col = add_plain_text_column (tree_view, _("Callers"), PROFILE_CALLER_NAME);
1389 add_sample_column (tree_view, _("Self"), PROFILE_CALLER_SELF);
1390 add_sample_column (tree_view, _("Total"), PROFILE_CALLER_TOTAL);
1391 gtk_tree_view_column_set_expand (col, TRUE);
1393 g_signal_connect (tree_view, "row-activated",
1394 G_CALLBACK (profile_caller_row_activated), pwin);
1396 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1399 static void
1400 setup_leak_block_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1402 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
1403 GtkListStore *store;
1405 g_return_if_fail (selection != NULL);
1407 store = gtk_list_store_new (4,
1408 G_TYPE_POINTER,
1409 G_TYPE_INT,
1410 G_TYPE_STRING,
1411 G_TYPE_POINTER);
1413 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
1415 add_pointer_column (tree_view, _("Address"), LEAK_BLOCK_ADDR);
1416 add_plain_text_column (tree_view, _("Size"), LEAK_BLOCK_SIZE);
1417 add_plain_text_column (tree_view, _("Caller"), LEAK_BLOCK_CALLER);
1419 g_signal_connect (selection, "changed",
1420 G_CALLBACK (leak_block_selection_changed), pwin);
1422 gtk_tree_view_columns_autosize (tree_view);
1425 static void
1426 setup_leak_stack_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1428 GtkListStore *store;
1430 store = gtk_list_store_new (3,
1431 G_TYPE_STRING,
1432 G_TYPE_INT,
1433 G_TYPE_STRING);
1435 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
1437 add_plain_text_column (tree_view, _("Function"), LEAK_STACK_NAME);
1438 add_plain_text_column (tree_view, _("Line"), LEAK_STACK_LINE);
1439 add_plain_text_column (tree_view, _("File"), LEAK_STACK_FILE);
1441 g_signal_connect (tree_view, "row-activated",
1442 G_CALLBACK (leak_stack_row_activated), pwin);
1444 gtk_tree_view_columns_autosize (tree_view);
1447 static void
1448 set_default_size (GtkWindow *window)
1450 GdkScreen *screen;
1451 int monitor_num;
1452 GdkRectangle monitor;
1453 int width, height;
1454 GtkWidget *widget = GTK_WIDGET (window);
1456 screen = gtk_widget_get_screen (widget);
1457 monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
1459 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1461 width = monitor.width * 3 / 4;
1462 height = monitor.height * 3 / 4;
1464 gtk_window_resize (window, width, height);
1467 static ProcessWindow *
1468 process_window_new (void)
1470 gchar *fullfilename;
1471 GladeXML *xml;
1472 GtkWidget *vpaned;
1473 GtkWidget *hpaned;
1474 ProcessWindow *pwin;
1475 GError *err = NULL;
1477 pwin = g_new0 (ProcessWindow, 1);
1478 process_windows = g_slist_prepend (process_windows, pwin);
1480 pwin->process = NULL;
1481 pwin->profile = NULL;
1482 pwin->leaks = NULL;
1484 pwin->usage_max = 32*1024;
1485 pwin->usage_high = 0;
1486 pwin->usage_leaked = 0;
1488 xml = glade_xml_new (glade_file, "MainWindow", NULL);
1490 pwin->main_window = get_widget (xml, "MainWindow");
1491 gtk_widget_realize (pwin->main_window);
1493 fullfilename = g_strdup ("./memprof.png");
1495 if (!g_file_test (fullfilename, G_FILE_TEST_EXISTS))
1496 fullfilename = g_build_filename (DATADIR, "memprof.png", NULL);
1499 gtk_window_set_icon_from_file (GTK_WINDOW (pwin->main_window), fullfilename, &err);
1501 g_free (fullfilename);
1503 g_signal_connect (pwin->main_window, "delete_event",
1504 G_CALLBACK (hide_and_check_quit), pwin);
1507 set_default_size (GTK_WINDOW (pwin->main_window));
1509 g_object_set_data_full (G_OBJECT (pwin->main_window),
1510 "process-window",
1511 pwin, (GDestroyNotify)process_window_free);
1513 pwin->main_notebook = get_widget (xml, "main-notebook");
1515 pwin->n_allocations_label = get_widget (xml, "n-allocations-label");
1516 pwin->bytes_per_label = get_widget (xml, "bytes-per-label");
1517 pwin->total_bytes_label = get_widget (xml, "total-bytes-label");
1519 pwin->profile_status_label = get_widget (xml, "profile-status-label");
1521 /* setup profile tree views */
1522 pwin->profile_func_tree_view = get_widget (xml, "profile-func-tree-view");
1523 pwin->profile_descendants_tree_view =
1524 get_widget (xml, "profile-descendants-tree-view");
1525 pwin->profile_caller_tree_view = get_widget (xml, "profile-caller-tree-view");
1527 setup_profile_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_func_tree_view));
1528 setup_profile_descendants_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_descendants_tree_view));
1529 setup_profile_caller_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_caller_tree_view));
1531 /* leak tree views */
1532 pwin->leak_block_tree_view = get_widget (xml, "leak-block-tree-view");
1533 pwin->leak_stack_tree_view = get_widget (xml, "leak-stack-tree-view");
1535 setup_leak_block_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_block_tree_view));
1536 setup_leak_stack_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_stack_tree_view));
1538 pwin->usage_max_label = get_widget (xml, "usage-max-label");
1539 pwin->usage_area = get_widget (xml, "usage-area");
1541 g_signal_connect (pwin->usage_area, "expose_event",
1542 G_CALLBACK (on_usage_area_expose), pwin);
1544 vpaned = get_widget (xml, "profile-vpaned");
1545 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1547 hpaned = get_widget (xml, "profile-hpaned");
1548 gtk_paned_set_position (GTK_PANED (hpaned), 150);
1550 vpaned = get_widget (xml, "leaks-vpaned");
1551 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1553 /* If profiling time, not memory, hide all GUI related to leak
1554 * detection.
1556 if (profile_type != MP_PROFILE_MEMORY) {
1557 gtk_widget_hide (get_widget (xml, "leaks-vpaned"));
1558 gtk_widget_hide (get_widget (xml, "toolbar-leaks-button"));
1559 gtk_widget_hide (get_widget (xml, "save-leak-info"));
1560 gtk_widget_hide (get_widget (xml, "generate-leak-report"));
1561 gtk_widget_hide (get_widget (xml, "allocation-bar"));
1562 gtk_notebook_set_show_tabs (
1563 GTK_NOTEBOOK (get_widget (xml, "main-notebook")), FALSE);
1565 else {
1566 gtk_widget_hide (get_widget (xml, "profile-status-label"));
1567 gtk_widget_hide (get_widget (xml, "reset-profile-button"));
1570 pwin->time_graph = get_widget(xml, "time-graph");
1571 pwin->mem_map = get_widget(xml, "mem-map");
1572 g_signal_connect(pwin->time_graph, "expose_event",
1573 G_CALLBACK (time_graph_expose_event), pwin);
1574 g_signal_connect(pwin->mem_map, "expose_event",
1575 G_CALLBACK (mem_map_expose_event), pwin);
1577 glade_xml_signal_autoconnect (xml);
1578 g_object_unref (G_OBJECT (xml));
1580 return pwin;
1584 MPProcess *
1585 process_window_get_process (ProcessWindow *pwin)
1587 return pwin->process;
1590 gboolean
1591 process_window_visible (ProcessWindow *pwin)
1593 return GTK_WIDGET_VISIBLE (pwin->main_window);
1596 void
1597 process_window_show (ProcessWindow *pwin)
1599 if (!process_window_visible (pwin))
1600 gtk_widget_show (pwin->main_window);
1601 else
1602 gdk_window_show (pwin->main_window->window);
1605 void
1606 process_window_hide (ProcessWindow *pwin)
1608 if (process_window_visible (pwin))
1609 hide_and_check_quit (pwin->main_window);
1612 void
1613 check_quit (void)
1615 GList *toplevels, *tmplist;
1617 tmplist = toplevels = gtk_window_list_toplevels ();
1618 while (tmplist) {
1619 if (GTK_WIDGET_VISIBLE (toplevels->data))
1620 return;
1621 tmplist = tmplist->next;
1624 g_list_free (toplevels);
1626 gtk_main_quit ();
1629 gboolean
1630 hide_and_check_quit (GtkWidget *window)
1632 gtk_widget_hide (window);
1633 check_quit ();
1635 return TRUE;
1639 void
1640 process_window_maybe_detach (ProcessWindow *pwin)
1642 GtkWidget *dialog;
1643 const char *message;
1644 gint response;
1646 if (pwin->process->status == MP_PROCESS_EXITING)
1647 message = _("Really detach from finished process?");
1648 else
1649 message = _("Really detach from running process?");
1651 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1652 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1653 GTK_MESSAGE_QUESTION,
1654 GTK_BUTTONS_YES_NO,
1655 "%s", message);
1656 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1658 response = gtk_dialog_run (GTK_DIALOG (dialog));
1659 gtk_widget_destroy (dialog);
1661 if (response == GTK_RESPONSE_YES)
1662 process_detach (pwin->process);
1666 void
1667 process_window_maybe_kill (ProcessWindow *pwin)
1669 if (pwin->process->status == MP_PROCESS_EXITING)
1670 process_window_maybe_detach (pwin);
1671 else {
1672 GtkWidget *dialog;
1673 gint response;
1675 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1676 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1677 GTK_MESSAGE_QUESTION,
1678 GTK_BUTTONS_YES_NO,
1679 _("Really kill running process?"));
1680 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1681 GTK_RESPONSE_YES);
1683 response = gtk_dialog_run (GTK_DIALOG (dialog));
1684 gtk_widget_destroy (dialog);
1686 if (response == GTK_RESPONSE_YES)
1687 process_kill (pwin->process);
1691 void
1692 sigchld_handler (int signum)
1694 int old_errno = errno;
1696 while (1) {
1697 int pid = waitpid (WAIT_ANY, NULL, WNOHANG);
1698 if (pid < 0 && errno != ECHILD)
1699 g_error ("waitpid: %s\n", g_strerror (errno));
1700 else if (pid <= 0)
1701 break;
1704 errno = old_errno;
1707 static char *profile_type_string = NULL;
1708 static char *profile_rate_string = NULL;
1709 static int profile_interval = 1000;
1710 static gchar **profile_skip_funcs = NULL;
1712 static const GOptionEntry entries[] =
1714 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE, &default_follow_fork,
1715 N_("Create new windows for forked processes"), NULL },
1716 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE, &default_follow_exec,
1717 N_("Retain windows for processes after exec()"), NULL },
1718 { "profile", '\0', 0, G_OPTION_ARG_STRING, &profile_type_string,
1719 N_("Type of profiling information to collect"), "memory/cycles/time" },
1720 { "rate", '\0', 0, G_OPTION_ARG_STRING, &profile_rate_string,
1721 N_("Number of samples/sec for time profile (1k=1000)"), NULL },
1722 { "skip-funcs", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &profile_skip_funcs,
1723 N_("Functions allocating memory"), "function_name" },
1724 { NULL }
1727 static void
1728 parse_options (int *argc, char ***argv)
1730 GError *err = NULL;
1731 GOptionContext *context;
1733 context = g_option_context_new ("- A memory profiler");
1735 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
1736 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1737 g_option_context_parse (context, argc, argv, &err);
1740 static void
1741 initialize_skip_funcs ()
1743 gint i = 0;
1745 /* C library functions */
1746 skip_funcs = g_slist_append (skip_funcs, "malloc");
1747 skip_funcs = g_slist_append (skip_funcs, "calloc");
1748 skip_funcs = g_slist_append (skip_funcs, "realloc");
1749 skip_funcs = g_slist_append (skip_funcs, "strdup");
1750 skip_funcs = g_slist_append (skip_funcs, "strndup");
1752 /* glib */
1753 skip_funcs = g_slist_append (skip_funcs, "g_malloc");
1754 skip_funcs = g_slist_append (skip_funcs, "g_malloc0");
1755 skip_funcs = g_slist_append (skip_funcs, "g_realloc");
1756 skip_funcs = g_slist_append (skip_funcs, "g_strdup");
1757 skip_funcs = g_slist_append (skip_funcs, "g_strndup");
1758 skip_funcs = g_slist_append (skip_funcs, "g_slice_alloc");
1759 skip_funcs = g_slist_append (skip_funcs, "g_slice_alloc0");
1760 skip_funcs = g_slist_append (skip_funcs, "g_memdup");
1762 /* C++/STL */
1763 skip_funcs = g_slist_append (skip_funcs, "_Znwj");
1765 /* WebKit */
1766 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF16fastZeroedMallocEj");
1767 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF10fastStrDupEPKc");
1768 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF10fastCallocEjj");
1769 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF10fastMallocEj");
1770 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF11fastReallocEPvj");
1772 /* Qt */
1773 skip_funcs = g_slist_append (skip_funcs, "_Z7qMallocj");
1774 skip_funcs = g_slist_append (skip_funcs, "_Z8qReallocPvj");
1776 /* Harfbuzz */
1777 skip_funcs = g_slist_append (skip_funcs, "_hb_alloc");
1779 while (profile_skip_funcs && profile_skip_funcs [i]) {
1780 skip_funcs = g_slist_append (skip_funcs, profile_skip_funcs [i]);
1781 ++i;
1786 main(int argc, char **argv)
1788 ProcessWindow *initial_window;
1790 gtk_init (&argc, &argv);
1792 parse_options (&argc, &argv);
1794 /* Set up a handler for SIGCHLD to avoid zombie children
1796 signal (SIGCHLD, sigchld_handler);
1798 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1799 textdomain (GETTEXT_PACKAGE);
1801 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1802 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1803 #endif
1805 /* If the user didn't specify the profile type explicitely,
1806 * we guess from the executable name.
1808 if (!profile_type_string) {
1809 char *basename;
1810 basename = g_path_get_basename (argv[0]);
1812 if (strcmp (basename, "speedprof") == 0)
1813 profile_type_string = "cycles";
1814 else
1815 profile_type_string = "memory";
1817 g_free (basename);
1820 if (strcmp (profile_type_string, "memory") == 0)
1821 profile_type = MP_PROFILE_MEMORY;
1822 else if (strcmp (profile_type_string, "cycles") == 0)
1823 profile_type = MP_PROFILE_CYCLES;
1824 else if (strcmp (profile_type_string, "time") == 0)
1825 profile_type = MP_PROFILE_TIME;
1826 else {
1827 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1828 exit (1);
1831 initialize_skip_funcs();
1833 if (profile_rate_string) {
1834 int multiplier = 1;
1835 double rate;
1836 int len = strlen (profile_rate_string);
1837 char suffix[2] = { '\0', '\0' };
1838 char *end;
1840 if (len > 0 &&
1841 (profile_rate_string[len - 1] == 'k' ||
1842 profile_rate_string[len - 1] == 'K')) {
1843 suffix[0] = profile_rate_string[len - 1];
1844 multiplier = 1000;
1845 profile_rate_string[len - 1] = '\0';
1848 rate = strtod (profile_rate_string, &end);
1849 if (len == 0 || *end != '\0' ||
1850 rate * multiplier <= 1 || rate * multiplier > 1000000) {
1851 g_printerr ("Invalid rate: %s%s\n",
1852 profile_rate_string, suffix);
1853 exit (1);
1856 profile_interval = (int) (0.5 + 1000000 / (rate * multiplier));
1859 glade_file = "./memprof.glade";
1860 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1861 glade_file = g_build_filename (DATADIR, "memprof.glade", NULL);
1863 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1864 show_error (NULL, ERROR_FATAL, _("Cannot find memprof.glade"));
1867 global_server = mp_server_new ();
1868 mp_server_set_profile_type (global_server, profile_type);
1869 mp_server_set_interval (global_server, profile_interval);
1871 g_signal_connect (global_server, "process_created",
1872 G_CALLBACK (process_created_cb), NULL);
1874 initial_window = process_window_new ();
1876 gtk_widget_show (initial_window->main_window);
1878 if (argc > 1) {
1879 /* skip over -- */
1880 gint start = 1;
1881 if (strcmp (argv[start], "--") == 0)
1882 start = 2;
1884 run_file (initial_window, (char **)(argv + start));
1887 gtk_main ();
1889 return 0;