main.c: Add old default to the skip funcs, add command line option
[memprof.git] / src / main.c
blobe62d9c934cbd350107430a91eab5a1792069ec2e
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 = pwin->profile->n_bytes;
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 gtk_tree_model_get (GTK_TREE_MODEL (store), &selected,
405 PROFILE_FUNC_FUNC, &func,
406 -1);
408 was_sorted = get_sort_info (GTK_TREE_VIEW (pwin->profile_descendants_tree_view),
409 &old_sort_column, &old_sort_type);
411 /* fill descendants tree */
412 tree_store = gtk_tree_store_new (4,
413 G_TYPE_STRING,
414 G_TYPE_DOUBLE,
415 G_TYPE_DOUBLE,
416 G_TYPE_POINTER);
418 descendant_tree = profile_func_create_descendant_tree (func);
420 add_node (tree_store, n_samples, NULL, descendant_tree->roots->pdata[0]);
422 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), TRUE);
424 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_descendants_tree_view),
425 GTK_TREE_MODEL (tree_store));
427 /* Expand the toplevel of the descendant tree so we see the immediate
428 * descendants.
430 path = gtk_tree_path_new_from_indices (0, -1);
431 gtk_tree_view_expand_row (GTK_TREE_VIEW (pwin->profile_descendants_tree_view), path, FALSE);
432 gtk_tree_path_free (path);
434 if (was_sorted)
435 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
436 old_sort_column, old_sort_type);
437 else
438 gtk_tree_sortable_set_sort_column_id (
439 GTK_TREE_SORTABLE (tree_store), 2, GTK_SORT_DESCENDING);
441 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (tree_store));
443 g_object_unref (G_OBJECT (tree_store));
445 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_descendants_tree_view));
447 profile_descendant_tree_free (descendant_tree);
449 /* fill caller tree */
450 was_sorted = get_sort_info (GTK_TREE_VIEW (pwin->profile_caller_tree_view),
451 &old_sort_column, &old_sort_type);
453 list_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER);
454 list_model = GTK_TREE_MODEL (list_store);
456 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->profile_caller_tree_view));
458 caller_list = profile_func_create_caller_list (func);
460 for (i = 0; i < caller_list->len; ++i) {
461 GtkTreeIter iter;
462 gchar *name;
463 ProfileFunc *caller = caller_list->pdata[i];
465 if (caller->node) {
466 if (caller->node->symbol)
467 name = elf_demangle(caller->node->symbol);
468 else
469 name = g_strdup("???");
471 else
472 name = g_strdup("<spontaneous>");
474 gtk_list_store_append (list_store, &iter);
476 gtk_list_store_set (list_store, &iter,
477 PROFILE_CALLER_NAME, name,
478 PROFILE_CALLER_SYMBOL, caller->node ? caller->node->symbol : GINT_TO_POINTER (-1),
479 -1);
481 set_sample (list_model, &iter, PROFILE_CALLER_SELF, caller->self, n_samples);
482 set_sample (list_model, &iter, PROFILE_CALLER_TOTAL, caller->total, n_samples);
484 g_free(name);
486 profile_caller_list_free (caller_list);
488 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_caller_tree_view));
490 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_caller_tree_view),
491 list_model);
493 if (was_sorted)
494 gtk_tree_sortable_set_sort_column_id (
495 GTK_TREE_SORTABLE (list_store), old_sort_column, old_sort_type);
496 else
497 gtk_tree_sortable_set_sort_column_id (
498 GTK_TREE_SORTABLE (list_store), 2, GTK_SORT_DESCENDING);
500 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (list_store));
502 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin->profile_caller_tree_view));
504 g_object_unref (G_OBJECT (list_store));
506 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_caller_tree_view), TRUE);
509 static void
510 profile_fill (ProcessWindow *pwin)
512 GtkListStore *store;
513 GtkTreeModel *model;
515 int i;
516 int n_samples = pwin->profile->n_bytes;
518 int old_sort_column;
519 GtkSortType old_sort_type;
520 gboolean was_sorted;
522 was_sorted =
523 get_sort_info (GTK_TREE_VIEW (pwin->profile_func_tree_view),
524 &old_sort_column, &old_sort_type);
526 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_func_tree_view), NULL);
527 store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER);
528 model = GTK_TREE_MODEL (store);
530 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_func_tree_view), model);
532 /* inserting in a ListStore is O(n) when sorting ... */
533 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->profile_func_tree_view));
535 for (i = 0; i < pwin->profile->functions->len; ++i) {
536 GtkTreeIter iter;
537 gchar *name;
539 ProfileFunc *func = pwin->profile->functions->pdata[i];
541 gtk_list_store_append (store, &iter);
543 g_assert (func);
545 if (func->node->symbol)
546 name = elf_demangle(func->node->symbol);
547 else
548 name = g_strdup("???");
550 gtk_list_store_set (store, &iter,
551 PROFILE_FUNC_NAME, name,
552 PROFILE_FUNC_FUNC, func,
553 -1);
555 set_sample (model, &iter, PROFILE_FUNC_SELF, func->self, n_samples);
556 set_sample (model, &iter, PROFILE_FUNC_TOTAL, func->total, n_samples);
558 g_free(name);
561 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_func_tree_view));
563 if (was_sorted) {
564 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), old_sort_column,
565 old_sort_type);
567 else {
568 gtk_tree_sortable_set_sort_column_id (
569 GTK_TREE_SORTABLE (store), 2, GTK_SORT_DESCENDING);
572 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (store));
574 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_func_tree_view), TRUE);
576 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin->profile_func_tree_view));
578 g_object_unref (G_OBJECT (store));
582 /************************************************************
583 * GUI for leak detection
584 ************************************************************/
586 static Block *
587 leak_block_get_selected (ProcessWindow *pwin)
589 GtkTreeSelection *selection;
590 GtkTreeModel *model;
591 GtkTreeIter iter;
592 Block *block = NULL;
594 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pwin->leak_block_tree_view));
595 if (selection && gtk_tree_selection_get_selected (selection, &model, &iter))
596 gtk_tree_model_get (model, &iter, LEAK_BLOCK_BLOCK, &block, -1);
598 return block;
601 static void
602 leak_block_selection_changed (GtkTreeSelection *selection,
603 ProcessWindow *pwin)
605 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
606 GtkListStore *store = GTK_LIST_STORE (model);
607 Block *block = leak_block_get_selected (pwin);
608 StackNode *stack;
610 gtk_list_store_clear (store);
612 if (block == NULL)
613 return;
615 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
617 for (stack = block->stack; stack != NULL; stack = stack->parent) {
618 GtkTreeIter iter;
619 const char *filename;
620 char *functionname;
621 unsigned int line;
623 if (!process_find_line (pwin->process, stack->address,
624 &filename, &functionname, &line)) {
625 /* 0x3f == '?' -- suppress trigraph warnings */
626 functionname = "(\x3f\x3f\x3f)";
627 filename = "(\x3f\x3f\x3f)";
628 line = 0;
631 gtk_list_store_append (store, &iter);
632 gtk_list_store_set (store, &iter,
633 LEAK_STACK_NAME, functionname,
634 LEAK_STACK_LINE, line,
635 LEAK_STACK_FILE, filename,
636 -1);
639 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
642 static void
643 leaks_fill (ProcessWindow *pwin)
645 GSList *tmp_list;
646 GtkTreeModel *model;
647 GtkListStore *store;
649 model = gtk_tree_view_get_model (GTK_TREE_VIEW (pwin->leak_block_tree_view));
650 store = GTK_LIST_STORE (model);
652 gtk_list_store_clear (store);
654 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->leak_block_tree_view));
656 tmp_list = pwin->leaks;
657 while (tmp_list) {
658 const char *filename;
659 gboolean free_function = FALSE;
660 char *functionname = NULL;
661 GtkTreeIter iter;
663 unsigned int line;
665 Block *block = tmp_list->data;
666 StackNode *stack;
668 for (stack = block->stack; stack != NULL; stack = stack->parent) {
669 if (process_find_line (pwin->process, stack->address,
670 &filename, &functionname, &line)) {
671 GSList *tmp_list;
673 if (!functionname)
674 continue;
676 for (tmp_list = skip_funcs; tmp_list != NULL; tmp_list = tmp_list->next) {
677 if (!strcmp (functionname, tmp_list->data)) {
678 functionname = NULL;
679 break;
683 if (!functionname)
684 continue;
686 for (tmp_list = skip_regexes; tmp_list != NULL; tmp_list = tmp_list->next) {
687 regex_t regex;
689 regcomp (&regex, tmp_list->data, 0);
691 if (!regexec (&regex, functionname, 0, NULL, 0)) {
692 functionname = NULL;
693 regfree (&regex);
694 break;
697 regfree (&regex);
701 if (functionname)
702 break;
704 if (!functionname) {
705 free_function = TRUE;
706 functionname = g_strdup ("(\x3f\x3f\x3f)");
709 gtk_list_store_append (store, &iter);
710 gtk_list_store_set (store, &iter,
711 LEAK_BLOCK_ADDR, block->addr,
712 LEAK_BLOCK_SIZE, block->size,
713 LEAK_BLOCK_CALLER, functionname,
714 LEAK_BLOCK_BLOCK, block,
715 -1);
717 if (free_function)
718 g_free (functionname);
720 tmp_list = tmp_list->next;
723 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->leak_block_tree_view));
726 static void
727 leak_stack_run_command (ProcessWindow *pwin, Block *block, int frame)
729 const char *filename;
730 char *functionname;
731 unsigned int line;
733 StackNode *stack = block->stack;
734 while (frame--)
735 stack = stack->parent;
737 if (process_find_line (pwin->process, stack->address,
738 &filename, &functionname, &line)) {
740 GString *command = g_string_new (NULL);
741 char *p = stack_command;
742 char buf[32];
743 char *cmdline;
744 GError *err = NULL;
746 while (*p) {
747 if (*p == '%') {
748 switch (*++p) {
749 case 'f':
750 g_string_append (command, filename);
751 break;
752 case 'l':
753 snprintf(buf, 32, "%d", line);
754 g_string_append (command, buf);
755 break;
756 case '%':
757 g_string_append_c (command, '%');
758 break;
759 default:
760 g_string_append_c (command, '%');
761 g_string_append_c (command, *p);
763 } else
764 g_string_append_c (command, *p);
765 p++;
768 cmdline = g_strdup_printf ("/bin/sh -c %s", command->str);
770 if (!g_spawn_command_line_async (cmdline, &err)) {
771 show_error (pwin->main_window,
772 ERROR_MODAL, _("Execution of \"%s\" failed: %s"),
773 command->str, err->message);
775 g_error_free (err);
778 g_free (cmdline);
780 g_string_free (command, FALSE);
784 static void
785 leak_stack_row_activated (GtkTreeView *tree_view,
786 GtkTreePath *path,
787 int column,
788 ProcessWindow *pwin)
790 GtkTreeModel *model;
791 GtkTreeIter iter;
792 int frame;
793 Block *block;
795 model = gtk_tree_view_get_model (tree_view);
796 gtk_tree_model_get_iter (model, &iter, path);
797 frame = list_iter_get_index (model, &iter);
799 block = leak_block_get_selected (pwin);
800 if (block)
801 leak_stack_run_command (pwin, block, frame);
806 /************************************************************
807 * File Selection handling
808 ************************************************************/
810 static gchar *
811 get_filename (const gchar *title,
812 const gchar *suggested_name)
814 GtkWidget *dialog;
815 gchar *filename = NULL;
816 dialog = gtk_file_chooser_dialog_new (title,
817 NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
818 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
819 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
820 NULL);
821 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), suggested_name);
822 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
823 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
825 gtk_widget_destroy (dialog);
826 return filename;
829 /* Really ugly utility function to retrieve the ProcessWindow from
830 * either a menu_item or toolbar item.
832 ProcessWindow *
833 pwin_from_widget (GtkWidget *widget)
835 GtkWidget *app;
837 if (GTK_IS_MENU_ITEM (widget)) {
838 GtkWidget *menu_shell = widget->parent;
840 while (menu_shell && !GTK_IS_MENU_BAR (menu_shell)) {
841 menu_shell = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))->parent;
843 g_assert (menu_shell != NULL);
845 app = gtk_widget_get_toplevel (menu_shell);
846 } else
847 app = gtk_widget_get_toplevel (widget);
849 return g_object_get_data (G_OBJECT (app), "process-window");
852 void
853 close_cb (GtkWidget *widget)
855 ProcessWindow *pwin = pwin_from_widget (widget);
857 hide_and_check_quit (pwin->main_window);
860 void
861 exit_cb (GtkWidget *widget)
863 gtk_main_quit ();
866 static void
867 reset_cb (MPProcess *process, ProcessWindow *pwin)
869 process_window_reset (pwin);
872 static void
873 status_changed_cb (MPProcess *process, ProcessWindow *pwin)
875 if (process->status == MP_PROCESS_DEFUNCT ||
876 process->status == MP_PROCESS_DETACHED) {
878 if (g_slist_length (process_windows) > 1) {
879 tree_window_remove (pwin);
880 process_window_destroy (pwin);
881 } else {
882 tree_window_remove (pwin);
884 if (pwin->process) {
885 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin->process), G_CALLBACK (status_changed_cb), pwin);
886 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin->process), G_CALLBACK (reset_cb), pwin);
887 g_object_unref (G_OBJECT (pwin->process));
888 pwin->process = NULL;
891 if (pwin->status_update_timeout) {
892 g_source_remove (pwin->status_update_timeout);
893 pwin->status_update_timeout = 0;
896 process_window_reset (pwin);
899 } else {
900 char *status = process_get_status_text (process);
901 char *cmdline = process_get_cmdline (process);
902 char *title = g_strdup_printf ("%s - %s (%d) - %s", _("MemProf"), cmdline, process->pid, status);
903 gtk_window_set_title (GTK_WINDOW (pwin->main_window), title);
905 g_free (title);
906 g_free (status);
907 g_free (cmdline);
911 static void
912 list_view_clear (GtkTreeView *tree_view)
914 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
915 gtk_list_store_clear (GTK_LIST_STORE (model));
918 static void
919 process_window_reset (ProcessWindow *pwin)
921 if (pwin->profile) {
922 profile_free (pwin->profile);
923 pwin->profile = NULL;
925 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_func_tree_view), NULL);
926 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_caller_tree_view), NULL);
927 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_descendants_tree_view), NULL);
929 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_func_tree_view), FALSE);
930 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_caller_tree_view), FALSE);
931 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), FALSE);
934 if (pwin->leaks) {
935 g_slist_free (pwin->leaks);
936 pwin->leaks = NULL;
937 list_view_clear (GTK_TREE_VIEW (pwin->leak_block_tree_view));
938 list_view_clear (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
941 pwin->usage_max = 32*1024;
942 pwin->usage_high = 0;
943 pwin->usage_leaked = 0;
945 gtk_window_set_title (GTK_WINDOW (pwin->main_window), "MemProf");
947 gtk_widget_queue_draw (pwin->usage_area);
948 dw_update(pwin);
951 static void
952 init_process (ProcessWindow *pwin, MPProcess *process)
954 pwin->process = process;
956 process_set_follow_fork (pwin->process, default_follow_fork);
957 process_set_follow_exec (pwin->process, default_follow_exec);
959 pwin->status_update_timeout =
960 g_timeout_add (100,
961 update_status,
962 pwin);
964 g_signal_connect (process, "status_changed",
965 G_CALLBACK (status_changed_cb), pwin);
966 g_signal_connect (process, "reset",
967 G_CALLBACK (reset_cb), pwin);
969 tree_window_add (pwin);
972 static void
973 process_created_cb (MPServer *server, MPProcess *process)
975 ProcessWindow *pwin = process_window_new ();
977 init_process (pwin, process);
979 tree_window_show ();
982 static gboolean
983 run_file (ProcessWindow *pwin, char **args)
985 gboolean result;
986 char *path;
988 g_return_val_if_fail (args != NULL, FALSE);
989 g_return_val_if_fail (args[0] != NULL, FALSE);
991 path = process_find_exec (args);
993 if (path) {
994 g_warning ("Process new '%s'\n", path);
995 MPProcess *process = process_new (global_server);
996 process_run (process, path, args);
998 if (pwin->process) {
999 pwin = process_window_new ();
1000 tree_window_show ();
1003 init_process (pwin, process);
1005 gtk_widget_show (pwin->main_window);
1007 result = TRUE;
1009 } else {
1010 show_error (pwin->main_window,
1011 ERROR_MODAL,
1012 _("Cannot find executable for \"%s\""),
1013 args[0]);
1014 result = FALSE;
1017 g_free (path);
1018 return result;
1022 void
1023 run_cb (GtkWidget *widget)
1025 GladeXML *xml;
1026 GtkWidget *run_dialog;
1027 GtkWidget *filechooser;
1029 ProcessWindow *pwin = pwin_from_widget (widget);
1031 xml = glade_xml_new (glade_file, "RunDialog", NULL);
1032 run_dialog = get_widget (xml, "RunDialog");
1033 filechooser = get_widget (xml, "RunDialog-chooser");
1035 g_object_unref (G_OBJECT (xml));
1037 while (1) {
1038 gtk_window_set_transient_for (GTK_WINDOW (run_dialog),
1039 GTK_WINDOW (pwin->main_window));
1040 if (gtk_dialog_run (GTK_DIALOG (run_dialog)) == 1) {
1041 gchar **args;
1042 char *text;
1043 gboolean result;
1045 text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechooser));
1046 if (!text)
1047 break;
1049 args = process_parse_exec (text);
1051 result = run_file (pwin, args);
1053 g_strfreev (args);
1054 g_free (text);
1056 if (result)
1057 break;
1058 } else {
1059 break;
1063 gtk_widget_destroy (run_dialog);
1066 void
1067 kill_cb (GtkWidget *widget)
1069 ProcessWindow *pwin = pwin_from_widget (widget);
1071 if (pwin->process)
1072 process_window_maybe_kill (pwin);
1075 void
1076 detach_cb (GtkWidget *widget)
1078 ProcessWindow *pwin = pwin_from_widget (widget);
1080 if (pwin->process)
1081 process_window_maybe_detach (pwin);
1084 void
1085 process_tree_cb (GtkWidget *widget)
1087 tree_window_show ();
1090 void
1091 save_leak_cb (GtkWidget *widget)
1093 static gchar *suggestion = NULL;
1094 gchar *filename;
1096 ProcessWindow *pwin = pwin_from_widget (widget);
1098 if (pwin->leaks) {
1099 filename = get_filename ("Save Leak Report",
1100 suggestion ? suggestion : "memprof.leak");
1101 if (filename) {
1102 g_free (suggestion);
1103 suggestion = filename;
1105 leaks_print (pwin->process, pwin->leaks, filename);
1106 g_free (filename);
1111 void
1112 save_profile_cb (GtkWidget *widget)
1114 static gchar *suggestion = NULL;
1115 gchar *filename;
1117 ProcessWindow *pwin = pwin_from_widget (widget);
1119 if (pwin->profile) {
1120 filename = get_filename ("Save Profile",
1121 suggestion ? suggestion : "memprof.out");
1122 if (filename) {
1123 g_free (suggestion);
1124 suggestion = filename;
1126 profile_write (pwin->profile, filename);
1127 g_free (filename);
1132 void
1133 save_current_cb (GtkWidget *widget)
1135 ProcessWindow *pwin = pwin_from_widget (widget);
1137 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin->main_notebook))) {
1138 case 0:
1139 save_profile_cb (widget);
1140 break;
1141 case 1:
1142 save_leak_cb (widget);
1143 break;
1147 void
1148 generate_leak_cb (GtkWidget *widget)
1150 GSList *tmp_list;
1151 ProcessWindow *pwin = pwin_from_widget (widget);
1154 if (pwin->process) {
1155 process_stop_input (pwin->process);
1157 while (pwin->leaks) {
1158 block_unref(pwin->leaks->data);
1159 pwin->leaks = g_slist_delete_link(pwin->leaks, pwin->leaks);
1161 pwin->leaks = leaks_find (pwin->process);
1163 pwin->usage_leaked = 0;
1164 tmp_list = pwin->leaks;
1165 while (tmp_list) {
1166 pwin->usage_leaked += ((Block *)tmp_list->data)->size;
1167 tmp_list = tmp_list->next;
1170 leaks_fill (pwin);
1171 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 1);
1173 process_start_input (pwin->process);
1177 void
1178 generate_profile_cb (GtkWidget *widget)
1180 ProcessWindow *pwin = pwin_from_widget (widget);
1182 if (pwin->process) {
1183 process_stop_input (pwin->process);
1185 if (pwin->profile) {
1186 profile_free (pwin->profile);
1187 pwin->profile = NULL;
1190 pwin->profile = profile_create (pwin->process, skip_funcs);
1191 process_start_input (pwin->process);
1192 profile_fill (pwin);
1194 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 0);
1198 void
1199 reset_profile_cb (GtkWidget *widget)
1201 ProcessWindow *pwin = pwin_from_widget (widget);
1203 process_window_reset (pwin);
1205 if (pwin->process)
1206 process_clear_input (pwin->process);
1209 void
1210 record_button_toggled_cb (GtkWidget *widget)
1212 ProcessWindow *pwin = pwin_from_widget (widget);
1214 if (gtk_toggle_tool_button_get_active
1215 (GTK_TOGGLE_TOOL_BUTTON (widget)))
1216 process_start_input (pwin->process);
1217 else
1218 process_stop_input (pwin->process);
1221 void
1222 about_cb (GtkWidget *widget)
1224 #define OSLASH "\303\270"
1225 ProcessWindow *pwin = pwin_from_widget (widget);
1227 /* FIXME: restore credits */
1228 gtk_show_about_dialog (GTK_WINDOW (pwin->main_window),
1229 #if 0
1230 "logo", pwin->icon,
1231 #endif
1232 "name", "MemProf",
1233 "copyright",
1234 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1235 "Copyright 2002, Kristian Rietveld\n"
1236 "Copyright 2002, 2006, 2007, S"OSLASH"ren Sandmann",
1237 #if 0
1238 "copyright", "Copyright 2004-2006, S"OSLASH"ren Sandmann",
1239 #endif
1240 "version", PACKAGE_VERSION,
1241 NULL);
1244 static void
1245 show_error_response (GtkDialog *dialog,
1246 gint response_id,
1247 gpointer user_data)
1249 if (response_id == GTK_RESPONSE_OK)
1250 gtk_widget_destroy (GTK_WIDGET (dialog));
1253 void
1254 show_error (GtkWidget *parent_window,
1255 ErrorType error,
1256 const gchar *format,
1257 ...)
1259 va_list args;
1260 char *message;
1261 GtkWidget *dialog;
1263 va_start (args, format);
1264 g_vasprintf (&message, format, args);
1265 va_end (args);
1267 dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL,
1268 GTK_DIALOG_DESTROY_WITH_PARENT,
1269 (error == ERROR_FATAL) ?
1270 GTK_MESSAGE_ERROR :
1271 GTK_MESSAGE_WARNING,
1272 GTK_BUTTONS_OK, "%s", message);
1273 g_free (message);
1275 gtk_window_set_title (GTK_WINDOW (dialog),
1276 (error == ERROR_FATAL) ?
1277 _("MemProf Error") : _("MemProf Warning"));
1279 if (error == ERROR_WARNING) {
1280 gtk_widget_show (dialog);
1281 g_signal_connect (dialog, "response",
1282 G_CALLBACK (show_error_response), NULL);
1283 } else {
1284 gtk_dialog_run (GTK_DIALOG (dialog));
1285 gtk_widget_destroy (dialog);
1288 if (error == ERROR_FATAL)
1289 exit(1);
1292 static void
1293 process_window_free (ProcessWindow *pwin)
1295 /* FIXME: we leak the process structure */
1297 if (pwin->leaks)
1298 g_slist_free (pwin->leaks);
1300 if (pwin->profile)
1301 profile_free (pwin->profile);
1303 process_windows = g_slist_remove (process_windows, pwin);
1304 if (!process_windows)
1305 gtk_main_quit ();
1307 g_free (pwin);
1311 static void
1312 process_window_destroy (ProcessWindow *pwin)
1314 if (pwin->status_update_timeout)
1315 g_source_remove (pwin->status_update_timeout);
1317 gtk_widget_destroy (pwin->main_window);
1318 check_quit ();
1321 static GtkTreeViewColumn *
1322 add_sample_column (GtkTreeView *view, const gchar *title, gint model_column)
1324 const char *format;
1326 if (profile_type == MP_PROFILE_MEMORY)
1327 format = "%.0f";
1328 else
1329 format = "%.2f";
1331 return add_double_format_column (view, title, model_column, format);
1334 static void
1335 setup_profile_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1337 GtkTreeSelection *selection;
1338 GtkTreeViewColumn *col;
1340 col = add_plain_text_column (tree_view, _("Functions"), PROFILE_FUNC_NAME);
1341 add_sample_column (tree_view, _("Self"), PROFILE_FUNC_SELF);
1342 add_sample_column (tree_view, _("Total"), PROFILE_FUNC_TOTAL);
1343 gtk_tree_view_column_set_expand (col, TRUE);
1345 selection = gtk_tree_view_get_selection (tree_view);
1346 g_return_if_fail (selection != NULL);
1347 g_signal_connect (selection, "changed", G_CALLBACK (profile_selection_changed), pwin);
1349 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1352 static void
1353 setup_profile_descendants_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1355 GtkTreeViewColumn *col;
1357 col = add_plain_text_column (tree_view, _("Descendants"), PROFILE_DESCENDANTS_NAME);
1358 add_sample_column (tree_view, _("Self"), PROFILE_DESCENDANTS_SELF);
1359 add_sample_column (tree_view, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE);
1360 gtk_tree_view_column_set_expand (col, TRUE);
1362 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), FALSE);
1364 g_signal_connect (tree_view, "row-activated",
1365 G_CALLBACK (profile_descendants_row_activated), pwin);
1367 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1370 static void
1371 setup_profile_caller_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1373 GtkTreeViewColumn *col;
1375 col = add_plain_text_column (tree_view, _("Callers"), PROFILE_CALLER_NAME);
1376 add_sample_column (tree_view, _("Self"), PROFILE_CALLER_SELF);
1377 add_sample_column (tree_view, _("Total"), PROFILE_CALLER_TOTAL);
1378 gtk_tree_view_column_set_expand (col, TRUE);
1380 g_signal_connect (tree_view, "row-activated",
1381 G_CALLBACK (profile_caller_row_activated), pwin);
1383 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1386 static void
1387 setup_leak_block_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1389 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
1390 GtkListStore *store;
1392 g_return_if_fail (selection != NULL);
1394 store = gtk_list_store_new (4,
1395 G_TYPE_POINTER,
1396 G_TYPE_INT,
1397 G_TYPE_STRING,
1398 G_TYPE_POINTER);
1400 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
1402 add_pointer_column (tree_view, _("Address"), LEAK_BLOCK_ADDR);
1403 add_plain_text_column (tree_view, _("Size"), LEAK_BLOCK_SIZE);
1404 add_plain_text_column (tree_view, _("Caller"), LEAK_BLOCK_CALLER);
1406 g_signal_connect (selection, "changed",
1407 G_CALLBACK (leak_block_selection_changed), pwin);
1409 gtk_tree_view_columns_autosize (tree_view);
1412 static void
1413 setup_leak_stack_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1415 GtkListStore *store;
1417 store = gtk_list_store_new (3,
1418 G_TYPE_STRING,
1419 G_TYPE_INT,
1420 G_TYPE_STRING);
1422 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
1424 add_plain_text_column (tree_view, _("Function"), LEAK_STACK_NAME);
1425 add_plain_text_column (tree_view, _("Line"), LEAK_STACK_LINE);
1426 add_plain_text_column (tree_view, _("File"), LEAK_STACK_FILE);
1428 g_signal_connect (tree_view, "row-activated",
1429 G_CALLBACK (leak_stack_row_activated), pwin);
1431 gtk_tree_view_columns_autosize (tree_view);
1434 static void
1435 set_default_size (GtkWindow *window)
1437 GdkScreen *screen;
1438 int monitor_num;
1439 GdkRectangle monitor;
1440 int width, height;
1441 GtkWidget *widget = GTK_WIDGET (window);
1443 screen = gtk_widget_get_screen (widget);
1444 monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
1446 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1448 width = monitor.width * 3 / 4;
1449 height = monitor.height * 3 / 4;
1451 gtk_window_resize (window, width, height);
1454 static ProcessWindow *
1455 process_window_new (void)
1457 gchar *fullfilename;
1458 GladeXML *xml;
1459 GtkWidget *vpaned;
1460 GtkWidget *hpaned;
1461 ProcessWindow *pwin;
1462 GError *err = NULL;
1464 pwin = g_new0 (ProcessWindow, 1);
1465 process_windows = g_slist_prepend (process_windows, pwin);
1467 pwin->process = NULL;
1468 pwin->profile = NULL;
1469 pwin->leaks = NULL;
1471 pwin->usage_max = 32*1024;
1472 pwin->usage_high = 0;
1473 pwin->usage_leaked = 0;
1475 xml = glade_xml_new (glade_file, "MainWindow", NULL);
1477 pwin->main_window = get_widget (xml, "MainWindow");
1478 gtk_widget_realize (pwin->main_window);
1480 fullfilename = g_strdup ("./memprof.png");
1482 if (!g_file_test (fullfilename, G_FILE_TEST_EXISTS))
1483 fullfilename = g_build_filename (DATADIR, "memprof.png", NULL);
1486 gtk_window_set_icon_from_file (GTK_WINDOW (pwin->main_window), fullfilename, &err);
1488 g_free (fullfilename);
1490 g_signal_connect (pwin->main_window, "delete_event",
1491 G_CALLBACK (hide_and_check_quit), pwin);
1494 set_default_size (GTK_WINDOW (pwin->main_window));
1496 g_object_set_data_full (G_OBJECT (pwin->main_window),
1497 "process-window",
1498 pwin, (GDestroyNotify)process_window_free);
1500 pwin->main_notebook = get_widget (xml, "main-notebook");
1502 pwin->n_allocations_label = get_widget (xml, "n-allocations-label");
1503 pwin->bytes_per_label = get_widget (xml, "bytes-per-label");
1504 pwin->total_bytes_label = get_widget (xml, "total-bytes-label");
1506 pwin->profile_status_label = get_widget (xml, "profile-status-label");
1508 /* setup profile tree views */
1509 pwin->profile_func_tree_view = get_widget (xml, "profile-func-tree-view");
1510 pwin->profile_descendants_tree_view =
1511 get_widget (xml, "profile-descendants-tree-view");
1512 pwin->profile_caller_tree_view = get_widget (xml, "profile-caller-tree-view");
1514 setup_profile_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_func_tree_view));
1515 setup_profile_descendants_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_descendants_tree_view));
1516 setup_profile_caller_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_caller_tree_view));
1518 /* leak tree views */
1519 pwin->leak_block_tree_view = get_widget (xml, "leak-block-tree-view");
1520 pwin->leak_stack_tree_view = get_widget (xml, "leak-stack-tree-view");
1522 setup_leak_block_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_block_tree_view));
1523 setup_leak_stack_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_stack_tree_view));
1525 pwin->usage_max_label = get_widget (xml, "usage-max-label");
1526 pwin->usage_area = get_widget (xml, "usage-area");
1528 g_signal_connect (pwin->usage_area, "expose_event",
1529 G_CALLBACK (on_usage_area_expose), pwin);
1531 vpaned = get_widget (xml, "profile-vpaned");
1532 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1534 hpaned = get_widget (xml, "profile-hpaned");
1535 gtk_paned_set_position (GTK_PANED (hpaned), 150);
1537 vpaned = get_widget (xml, "leaks-vpaned");
1538 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1540 /* If profiling time, not memory, hide all GUI related to leak
1541 * detection.
1543 if (profile_type != MP_PROFILE_MEMORY) {
1544 gtk_widget_hide (get_widget (xml, "leaks-vpaned"));
1545 gtk_widget_hide (get_widget (xml, "toolbar-leaks-button"));
1546 gtk_widget_hide (get_widget (xml, "save-leak-info"));
1547 gtk_widget_hide (get_widget (xml, "generate-leak-report"));
1548 gtk_widget_hide (get_widget (xml, "allocation-bar"));
1549 gtk_notebook_set_show_tabs (
1550 GTK_NOTEBOOK (get_widget (xml, "main-notebook")), FALSE);
1552 else {
1553 gtk_widget_hide (get_widget (xml, "profile-status-label"));
1554 gtk_widget_hide (get_widget (xml, "reset-profile-button"));
1557 pwin->time_graph = get_widget(xml, "time-graph");
1558 pwin->mem_map = get_widget(xml, "mem-map");
1559 g_signal_connect(pwin->time_graph, "expose_event",
1560 G_CALLBACK (time_graph_expose_event), pwin);
1561 g_signal_connect(pwin->mem_map, "expose_event",
1562 G_CALLBACK (mem_map_expose_event), pwin);
1564 glade_xml_signal_autoconnect (xml);
1565 g_object_unref (G_OBJECT (xml));
1567 return pwin;
1571 MPProcess *
1572 process_window_get_process (ProcessWindow *pwin)
1574 return pwin->process;
1577 gboolean
1578 process_window_visible (ProcessWindow *pwin)
1580 return GTK_WIDGET_VISIBLE (pwin->main_window);
1583 void
1584 process_window_show (ProcessWindow *pwin)
1586 if (!process_window_visible (pwin))
1587 gtk_widget_show (pwin->main_window);
1588 else
1589 gdk_window_show (pwin->main_window->window);
1592 void
1593 process_window_hide (ProcessWindow *pwin)
1595 if (process_window_visible (pwin))
1596 hide_and_check_quit (pwin->main_window);
1599 void
1600 check_quit (void)
1602 GList *toplevels, *tmplist;
1604 tmplist = toplevels = gtk_window_list_toplevels ();
1605 while (tmplist) {
1606 if (GTK_WIDGET_VISIBLE (toplevels->data))
1607 return;
1608 tmplist = tmplist->next;
1611 g_list_free (toplevels);
1613 gtk_main_quit ();
1616 gboolean
1617 hide_and_check_quit (GtkWidget *window)
1619 gtk_widget_hide (window);
1620 check_quit ();
1622 return TRUE;
1626 void
1627 process_window_maybe_detach (ProcessWindow *pwin)
1629 GtkWidget *dialog;
1630 const char *message;
1631 gint response;
1633 if (pwin->process->status == MP_PROCESS_EXITING)
1634 message = _("Really detach from finished process?");
1635 else
1636 message = _("Really detach from running process?");
1638 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1639 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1640 GTK_MESSAGE_QUESTION,
1641 GTK_BUTTONS_YES_NO,
1642 "%s", message);
1643 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1645 response = gtk_dialog_run (GTK_DIALOG (dialog));
1646 gtk_widget_destroy (dialog);
1648 if (response == GTK_RESPONSE_YES)
1649 process_detach (pwin->process);
1653 void
1654 process_window_maybe_kill (ProcessWindow *pwin)
1656 if (pwin->process->status == MP_PROCESS_EXITING)
1657 process_window_maybe_detach (pwin);
1658 else {
1659 GtkWidget *dialog;
1660 gint response;
1662 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1663 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1664 GTK_MESSAGE_QUESTION,
1665 GTK_BUTTONS_YES_NO,
1666 _("Really kill running process?"));
1667 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1668 GTK_RESPONSE_YES);
1670 response = gtk_dialog_run (GTK_DIALOG (dialog));
1671 gtk_widget_destroy (dialog);
1673 if (response == GTK_RESPONSE_YES)
1674 process_kill (pwin->process);
1678 void
1679 sigchld_handler (int signum)
1681 int old_errno = errno;
1683 while (1) {
1684 int pid = waitpid (WAIT_ANY, NULL, WNOHANG);
1685 if (pid < 0 && errno != ECHILD)
1686 g_error ("waitpid: %s\n", g_strerror (errno));
1687 else if (pid <= 0)
1688 break;
1691 errno = old_errno;
1694 static char *profile_type_string = NULL;
1695 static char *profile_rate_string = NULL;
1696 static int profile_interval = 1000;
1697 static gchar **profile_skip_funcs = NULL;
1699 static const GOptionEntry entries[] =
1701 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE, &default_follow_fork,
1702 N_("Create new windows for forked processes"), NULL },
1703 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE, &default_follow_exec,
1704 N_("Retain windows for processes after exec()"), NULL },
1705 { "profile", '\0', 0, G_OPTION_ARG_STRING, &profile_type_string,
1706 N_("Type of profiling information to collect"), "memory/cycles/time" },
1707 { "rate", '\0', 0, G_OPTION_ARG_STRING, &profile_rate_string,
1708 N_("Number of samples/sec for time profile (1k=1000)"), NULL },
1709 { "skip-funcs", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &profile_skip_funcs,
1710 N_("Functions allocating memory"), "function_name" },
1711 { NULL }
1714 static void
1715 parse_options (int *argc, char ***argv)
1717 GError *err = NULL;
1718 GOptionContext *context;
1720 context = g_option_context_new ("- A memory profiler");
1722 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
1723 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1724 g_option_context_parse (context, argc, argv, &err);
1727 static void
1728 initialize_skip_funcs ()
1730 gint i = 0;
1732 skip_funcs = g_slist_append (skip_funcs, "g_malloc");
1733 skip_funcs = g_slist_append (skip_funcs, "g_malloc0");
1734 skip_funcs = g_slist_append (skip_funcs, "g_realloc");
1735 skip_funcs = g_slist_append (skip_funcs, "g_strdup");
1736 skip_funcs = g_slist_append (skip_funcs, "g_strndup");
1737 skip_funcs = g_slist_append (skip_funcs, "g_slice_alloc");
1738 skip_funcs = g_slist_append (skip_funcs, "g_slice_alloc0");
1739 skip_funcs = g_slist_append (skip_funcs, "strdup");
1740 skip_funcs = g_slist_append (skip_funcs, "strndup");
1741 skip_funcs = g_slist_append (skip_funcs, "_Znwj");
1742 skip_funcs = g_slist_append (skip_funcs, "_ZN3WTF16fastZeroedMallocEj");
1744 while (profile_skip_funcs && profile_skip_funcs [i]) {
1745 skip_funcs = g_slist_append (skip_funcs, profile_skip_funcs [i]);
1746 ++i;
1751 main(int argc, char **argv)
1753 ProcessWindow *initial_window;
1755 gtk_init (&argc, &argv);
1757 parse_options (&argc, &argv);
1759 /* Set up a handler for SIGCHLD to avoid zombie children
1761 signal (SIGCHLD, sigchld_handler);
1763 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1764 textdomain (GETTEXT_PACKAGE);
1766 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1767 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1768 #endif
1770 /* If the user didn't specify the profile type explicitely,
1771 * we guess from the executable name.
1773 if (!profile_type_string) {
1774 char *basename;
1775 basename = g_path_get_basename (argv[0]);
1777 if (strcmp (basename, "speedprof") == 0)
1778 profile_type_string = "cycles";
1779 else
1780 profile_type_string = "memory";
1782 g_free (basename);
1785 if (strcmp (profile_type_string, "memory") == 0)
1786 profile_type = MP_PROFILE_MEMORY;
1787 else if (strcmp (profile_type_string, "cycles") == 0)
1788 profile_type = MP_PROFILE_CYCLES;
1789 else if (strcmp (profile_type_string, "time") == 0)
1790 profile_type = MP_PROFILE_TIME;
1791 else {
1792 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1793 exit (1);
1796 initialize_skip_funcs();
1798 if (profile_rate_string) {
1799 int multiplier = 1;
1800 double rate;
1801 int len = strlen (profile_rate_string);
1802 char suffix[2] = { '\0', '\0' };
1803 char *end;
1805 if (len > 0 &&
1806 (profile_rate_string[len - 1] == 'k' ||
1807 profile_rate_string[len - 1] == 'K')) {
1808 suffix[0] = profile_rate_string[len - 1];
1809 multiplier = 1000;
1810 profile_rate_string[len - 1] = '\0';
1813 rate = strtod (profile_rate_string, &end);
1814 if (len == 0 || *end != '\0' ||
1815 rate * multiplier <= 1 || rate * multiplier > 1000000) {
1816 g_printerr ("Invalid rate: %s%s\n",
1817 profile_rate_string, suffix);
1818 exit (1);
1821 profile_interval = (int) (0.5 + 1000000 / (rate * multiplier));
1824 glade_file = "./memprof.glade";
1825 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1826 glade_file = g_build_filename (DATADIR, "memprof.glade", NULL);
1828 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1829 show_error (NULL, ERROR_FATAL, _("Cannot find memprof.glade"));
1832 global_server = mp_server_new ();
1833 mp_server_set_profile_type (global_server, profile_type);
1834 mp_server_set_interval (global_server, profile_interval);
1836 g_signal_connect (global_server, "process_created",
1837 G_CALLBACK (process_created_cb), NULL);
1839 initial_window = process_window_new ();
1841 gtk_widget_show (initial_window->main_window);
1843 if (argc > 1)
1844 run_file (initial_window, (char **)(argv + 1));
1846 gtk_main ();
1848 return 0;