process.c: Use the symbol, it looks like it was meant to be used...
[memprof.git] / src / main.c
blob77a14acb30b730b43c4e5f4db86f28924cad5e25
1 /* -*- mode: C; c-file-style: "linux" -*- */
3 /* MemProf -- memory profiler and leak detector
4 * Copyright 1999, 2000, 2001, Red Hat, Inc.
5 * Copyright 2002, Kristian Rietveld
6 * Copyright 2002, Soeren Sandmann (sandmann@daimi.au.dk)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 /*====*/
24 #define _GNU_SOURCE
26 #include "config.h"
28 #include <stdarg.h>
29 #include <stdio.h>
31 #include <errno.h>
32 #include <sys/wait.h>
33 #include <signal.h>
35 #include <glade/glade.h>
37 #include <regex.h>
39 #include "leakdetect.h"
40 #include "gui.h"
41 #include "memprof.h"
42 #include "process.h"
43 #include "profile.h"
44 #include "server.h"
45 #include "treeviewutils.h"
46 #include <string.h>
47 #include <stdlib.h>
48 #include <glib/gi18n.h>
49 #include <glib/gprintf.h>
50 #include "elfparser.h"
52 struct _ProcessWindow {
53 MPProcess *process;
54 Profile *profile;
55 GSList *leaks;
57 GtkWidget *main_window;
58 GtkWidget *main_notebook;
59 GtkWidget *n_allocations_label;
60 GtkWidget *profile_status_label;
61 GtkWidget *bytes_per_label;
62 GtkWidget *total_bytes_label;
64 GtkWidget *profile_func_tree_view;
65 GtkWidget *profile_caller_tree_view;
66 GtkWidget *profile_descendants_tree_view;
68 GtkWidget *leak_block_tree_view;
69 GtkWidget *leak_stack_tree_view;
71 GtkWidget *usage_max_label;
72 GtkWidget *usage_area;
74 guint usage_max;
75 guint usage_high;
76 guint usage_leaked;
78 guint status_update_timeout;
81 enum {
82 PROFILE_FUNC_NAME,
83 PROFILE_FUNC_SELF,
84 PROFILE_FUNC_TOTAL,
85 PROFILE_FUNC_FUNC
88 enum {
89 PROFILE_CALLER_NAME,
90 PROFILE_CALLER_SELF,
91 PROFILE_CALLER_TOTAL,
92 PROFILE_CALLER_SYMBOL
95 enum {
96 PROFILE_DESCENDANTS_NAME,
97 PROFILE_DESCENDANTS_SELF,
98 PROFILE_DESCENDANTS_NONRECURSE,
99 PROFILE_DESCENDANTS_SYMBOL
102 enum {
103 LEAK_BLOCK_ADDR,
104 LEAK_BLOCK_SIZE,
105 LEAK_BLOCK_CALLER,
106 LEAK_BLOCK_BLOCK,
109 enum {
110 LEAK_STACK_NAME,
111 LEAK_STACK_LINE,
112 LEAK_STACK_FILE
115 char *glade_file;
117 MPServer *global_server;
119 static ProcessWindow *process_window_new (void);
120 static void process_window_destroy (ProcessWindow *pwin);
121 static void process_window_reset (ProcessWindow *pwin);
123 static GSList *skip_funcs = NULL;
124 static GSList *skip_regexes = NULL;
126 static gboolean default_follow_fork = FALSE;
127 static gboolean default_follow_exec = FALSE;
129 char *stack_command = NULL;
131 GSList *process_windows = NULL;
133 static MPProfileType profile_type;
136 /* Helper function to print a clear error message if there
137 * is a problem looking up a particular widget
139 static GtkWidget *
140 get_widget (GladeXML *glade,
141 const char *name)
143 GtkWidget *widget = glade_xml_get_widget (glade, name);
144 if (!widget)
145 g_error ("Cannot lookup %s\n", name);
147 return widget;
151 /************************************************************
152 * Status Page
153 ************************************************************/
155 static void
156 draw_bar (GtkWidget *widget, int red, int green, int blue,
157 int size)
159 GdkGC *gc = gdk_gc_new (widget->window);
160 GdkColor color;
162 color.red = red;
163 color.green = green;
164 color.blue = blue;
166 gdk_gc_set_rgb_fg_color (gc, &color);
168 gdk_draw_rectangle (widget->window, gc, TRUE,
169 0, 0, size, widget->allocation.height);
171 color.red = 0;
172 color.green = 0;
173 color.blue = 0;
175 gdk_gc_set_rgb_fg_color (gc, &color);
177 gdk_draw_rectangle (widget->window, gc, FALSE,
178 0, 0, size - 1, widget->allocation.height - 1);
180 g_object_unref (gc);
183 static gboolean
184 on_usage_area_expose (GtkWidget *widget, GdkEvent *event,
185 gpointer data)
187 ProcessWindow *pwin = data;
188 int width;
189 int bytes_used;
190 int leak_size;
191 int high_size;
192 int current_size;
194 width = widget->allocation.width;
196 /* background */
197 draw_bar (widget, 0xffff, 0xffff, 0xffff, width);
199 /* bars */
200 if (pwin->process)
201 bytes_used = pwin->process->bytes_used;
202 else
203 bytes_used = 0;
205 /* bars */
206 leak_size = (width * ((double)pwin->usage_leaked / pwin->usage_max));
207 high_size = (width * ((double)pwin->usage_high / pwin->usage_max));
208 current_size = (width * ((double)bytes_used / pwin->usage_max));
210 draw_bar (widget, 0x0000, 0x0000, 0xffff, high_size);
211 draw_bar (widget, 0xffff, 0xffff, 0x0000, current_size);
212 draw_bar (widget, 0xffff, 0x0000, 0x0000, leak_size);
214 return TRUE;
217 static gboolean
218 update_status (gpointer data)
220 char *tmp;
221 ProcessWindow *pwin = (ProcessWindow *)data;
223 tmp = g_strdup_printf ("%d", pwin->process->bytes_used);
224 gtk_label_set_text (GTK_LABEL (pwin->total_bytes_label), tmp);
225 g_free (tmp);
227 tmp = g_strdup_printf ("%d", pwin->process->n_allocations);
228 gtk_label_set_text (GTK_LABEL (pwin->n_allocations_label), tmp);
229 g_free (tmp);
231 tmp = g_strdup_printf ("%d samples", pwin->process->bytes_used);
232 gtk_label_set_text (GTK_LABEL (pwin->profile_status_label), tmp);
233 g_free (tmp);
235 if (pwin->process->n_allocations == 0)
236 tmp = g_strdup("-");
237 else
238 tmp = g_strdup_printf ("%.2f",
239 (double)pwin->process->bytes_used /
240 pwin->process->n_allocations);
241 gtk_label_set_text (GTK_LABEL (pwin->bytes_per_label), tmp);
242 g_free (tmp);
244 if (pwin->process->bytes_used > pwin->usage_max) {
245 while ((pwin->process->bytes_used > pwin->usage_max))
246 pwin->usage_max *= 2;
248 tmp = g_strdup_printf ("%dk", pwin->usage_max / 1024);
249 gtk_label_set_text (GTK_LABEL (pwin->usage_max_label), tmp);
250 g_free (tmp);
253 pwin->usage_high = MAX (pwin->process->bytes_used, pwin->usage_high);
255 gtk_widget_queue_draw (pwin->usage_area);
257 return TRUE;
261 /************************************************************
262 * GUI for profiles
263 ************************************************************/
265 static gboolean
266 get_sort_info (GtkTreeView *view, int *sort_column, GtkSortType *sort_type)
268 GtkTreeModel *model = gtk_tree_view_get_model (view);
270 if (model && GTK_IS_TREE_SORTABLE (model))
271 return gtk_tree_sortable_get_sort_column_id (
272 GTK_TREE_SORTABLE (model), sort_column, sort_type);
274 return FALSE;
277 static void
278 profile_func_list_goto_symbol (ProcessWindow *pwin, char *symbol)
280 GtkTreeModel *function_list;
281 GtkTreeIter iter;
282 gboolean found = FALSE;
284 function_list = gtk_tree_view_get_model (GTK_TREE_VIEW (pwin->profile_func_tree_view));
286 if (gtk_tree_model_get_iter_first (function_list, &iter)) {
287 do {
288 ProfileFunc *func;
290 gtk_tree_model_get (function_list, &iter,
291 PROFILE_FUNC_FUNC, &func,
292 -1);
294 if (symbol_equal (func->node->symbol, symbol)) {
295 found = TRUE;
296 break;
298 } while (gtk_tree_model_iter_next (function_list, &iter));
301 if (found) {
302 GtkTreePath *path =
303 gtk_tree_model_get_path (function_list, &iter);
305 gtk_tree_view_set_cursor (
306 GTK_TREE_VIEW (pwin->profile_func_tree_view), path, 0, FALSE);
308 gtk_widget_grab_focus (GTK_WIDGET (pwin->profile_func_tree_view));
311 static void
312 profile_descendants_row_activated (GtkTreeView *treeview,
313 GtkTreePath *path,
314 GtkTreeViewColumn *column,
315 gpointer data)
317 GtkTreeModel *descendants_store;
318 ProcessWindow *pwin = data;
319 GtkTreeIter iter;
320 char *desc_symbol;
322 descendants_store = gtk_tree_view_get_model (treeview);
323 if (!gtk_tree_model_get_iter (descendants_store, &iter, path))
324 return;
326 gtk_tree_model_get (descendants_store, &iter, PROFILE_DESCENDANTS_SYMBOL, &desc_symbol, -1);
328 profile_func_list_goto_symbol (pwin, desc_symbol);
331 static void
332 profile_caller_row_activated (GtkTreeView *treeview,
333 GtkTreePath *path,
334 GtkTreeViewColumn *column,
335 gpointer data)
337 GtkTreeModel *caller_list;
338 ProcessWindow *pwin = data;
339 GtkTreeIter iter;
340 char *caller_symbol;
342 caller_list = gtk_tree_view_get_model (treeview);
343 if (!gtk_tree_model_get_iter (caller_list, &iter, path))
344 return;
346 gtk_tree_model_get (caller_list, &iter, 3, &caller_symbol, -1);
348 if (caller_symbol != GINT_TO_POINTER (-1))
349 profile_func_list_goto_symbol (pwin, caller_symbol);
352 static void
353 set_double (GtkTreeModel *model, GtkTreeIter *iter, int column, double value)
355 if (GTK_IS_TREE_STORE (model))
356 gtk_tree_store_set (GTK_TREE_STORE (model), iter, column, value, -1);
357 else
358 gtk_list_store_set (GTK_LIST_STORE (model), iter, column, value, -1);
361 static void
362 set_sample (GtkTreeModel *model, GtkTreeIter *iter, int column, guint value, guint n_samples)
364 if (profile_type == MP_PROFILE_MEMORY)
365 set_double (model, iter, column, value);
366 else
367 set_double (model, iter, column, 100 * (double)value / n_samples);
370 static void
371 add_node (GtkTreeStore *store, int n_samples,
372 const GtkTreeIter *parent, ProfileDescendantTreeNode *node)
374 GtkTreeIter iter;
375 gchar *name;
376 int i;
378 g_return_if_fail (GTK_IS_TREE_STORE (store));
380 gtk_tree_store_insert (store, &iter, (GtkTreeIter *)parent, 0);
382 if (node->symbol)
383 name = elf_demangle (node->symbol);
384 else
385 name = g_strdup ("???");
387 if (profile_type == MP_PROFILE_MEMORY) {
388 gtk_tree_store_set (store, &iter,
389 PROFILE_DESCENDANTS_NAME, name,
390 PROFILE_DESCENDANTS_SELF, (double)node->self,
391 PROFILE_DESCENDANTS_NONRECURSE, (double)node->non_recursion,
392 PROFILE_DESCENDANTS_SYMBOL, node->symbol,
393 -1);
395 else {
396 gtk_tree_store_set (store, &iter,
397 PROFILE_DESCENDANTS_NAME, name,
398 PROFILE_DESCENDANTS_SELF, (100.0 * node->self) / n_samples,
399 PROFILE_DESCENDANTS_NONRECURSE, (100.0 * node->non_recursion) / n_samples,
400 PROFILE_DESCENDANTS_SYMBOL, node->symbol,
401 -1);
404 g_free (name);
406 for (i = 0; i < node->children->len; ++i)
407 add_node (store, n_samples, &iter, node->children->pdata[i]);
410 static void
411 profile_selection_changed (GtkTreeSelection *selection, ProcessWindow *pwin)
413 GtkListStore *store;
414 GtkTreeIter selected;
415 ProfileFunc *func;
416 GtkTreeStore *tree_store;
417 GtkListStore *list_store;
418 GtkTreeModel *list_model;
419 GtkTreePath *path;
420 GPtrArray *caller_list;
421 ProfileDescendantTree *descendant_tree;
422 int i;
423 int n_samples = pwin->profile->n_bytes;
424 int old_sort_column;
425 GtkSortType old_sort_type;
426 gboolean was_sorted;
428 if (!gtk_tree_selection_get_selected (selection, (GtkTreeModel **)&store, &selected))
430 g_warning ("No selection");
431 return;
434 gtk_tree_model_get (GTK_TREE_MODEL (store), &selected,
435 PROFILE_FUNC_FUNC, &func,
436 -1);
438 was_sorted = get_sort_info (GTK_TREE_VIEW (pwin->profile_descendants_tree_view),
439 &old_sort_column, &old_sort_type);
441 /* fill descendants tree */
442 tree_store = gtk_tree_store_new (4,
443 G_TYPE_STRING,
444 G_TYPE_DOUBLE,
445 G_TYPE_DOUBLE,
446 G_TYPE_POINTER);
448 descendant_tree = profile_func_create_descendant_tree (func);
450 add_node (tree_store, n_samples, NULL, descendant_tree->roots->pdata[0]);
452 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), TRUE);
454 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_descendants_tree_view),
455 GTK_TREE_MODEL (tree_store));
457 /* Expand the toplevel of the descendant tree so we see the immediate
458 * descendants.
460 path = gtk_tree_path_new_from_indices (0, -1);
461 gtk_tree_view_expand_row (GTK_TREE_VIEW (pwin->profile_descendants_tree_view), path, FALSE);
462 gtk_tree_path_free (path);
464 if (was_sorted)
465 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
466 old_sort_column, old_sort_type);
467 else
468 gtk_tree_sortable_set_sort_column_id (
469 GTK_TREE_SORTABLE (tree_store), 2, GTK_SORT_DESCENDING);
471 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (tree_store));
473 g_object_unref (G_OBJECT (tree_store));
475 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_descendants_tree_view));
477 profile_descendant_tree_free (descendant_tree);
479 /* fill caller tree */
480 was_sorted = get_sort_info (GTK_TREE_VIEW (pwin->profile_caller_tree_view),
481 &old_sort_column, &old_sort_type);
483 list_store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER);
484 list_model = GTK_TREE_MODEL (list_store);
486 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->profile_caller_tree_view));
488 caller_list = profile_func_create_caller_list (func);
490 for (i = 0; i < caller_list->len; ++i) {
491 GtkTreeIter iter;
492 gchar *name;
493 ProfileFunc *caller = caller_list->pdata[i];
495 if (caller->node) {
496 if (caller->node->symbol)
497 name = caller->node->symbol;
498 else
499 name = "???";
501 else
502 name = "<spontaneous>";
504 gtk_list_store_append (list_store, &iter);
506 gtk_list_store_set (list_store, &iter,
507 PROFILE_CALLER_NAME, name,
508 PROFILE_CALLER_SYMBOL, caller->node ? caller->node->symbol : GINT_TO_POINTER (-1),
509 -1);
511 set_sample (list_model, &iter, PROFILE_CALLER_SELF, caller->self, n_samples);
512 set_sample (list_model, &iter, PROFILE_CALLER_TOTAL, caller->total, n_samples);
514 profile_caller_list_free (caller_list);
516 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_caller_tree_view));
518 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_caller_tree_view),
519 list_model);
521 if (was_sorted)
522 gtk_tree_sortable_set_sort_column_id (
523 GTK_TREE_SORTABLE (list_store), old_sort_column, old_sort_type);
524 else
525 gtk_tree_sortable_set_sort_column_id (
526 GTK_TREE_SORTABLE (list_store), 2, GTK_SORT_DESCENDING);
528 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (list_store));
530 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin->profile_caller_tree_view));
532 g_object_unref (G_OBJECT (list_store));
534 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_caller_tree_view), TRUE);
537 static void
538 profile_fill (ProcessWindow *pwin)
540 GtkListStore *store;
541 GtkTreeModel *model;
543 int i;
544 int n_samples = pwin->profile->n_bytes;
546 int old_sort_column;
547 GtkSortType old_sort_type;
548 gboolean was_sorted;
550 was_sorted =
551 get_sort_info (GTK_TREE_VIEW (pwin->profile_func_tree_view),
552 &old_sort_column, &old_sort_type);
554 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_func_tree_view), NULL);
555 store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER);
556 model = GTK_TREE_MODEL (store);
558 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_func_tree_view), model);
560 /* inserting in a ListStore is O(n) when sorting ... */
561 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->profile_func_tree_view));
563 for (i = 0; i < pwin->profile->functions->len; ++i) {
564 GtkTreeIter iter;
565 gchar *name;
567 ProfileFunc *func = pwin->profile->functions->pdata[i];
569 gtk_list_store_append (store, &iter);
571 g_assert (func);
573 if (func->node->symbol)
574 name = func->node->symbol;
575 else
576 name = "???";
578 gtk_list_store_set (store, &iter,
579 PROFILE_FUNC_NAME, name,
580 PROFILE_FUNC_FUNC, func,
581 -1);
583 set_sample (model, &iter, PROFILE_FUNC_SELF, func->self, n_samples);
584 set_sample (model, &iter, PROFILE_FUNC_TOTAL, func->total, n_samples);
587 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->profile_func_tree_view));
589 if (was_sorted) {
590 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), old_sort_column,
591 old_sort_type);
593 else {
594 gtk_tree_sortable_set_sort_column_id (
595 GTK_TREE_SORTABLE (store), 2, GTK_SORT_DESCENDING);
598 gtk_tree_sortable_sort_column_changed (GTK_TREE_SORTABLE (store));
600 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_func_tree_view), TRUE);
602 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (pwin->profile_func_tree_view));
604 g_object_unref (G_OBJECT (store));
608 /************************************************************
609 * GUI for leak detection
610 ************************************************************/
612 static Block *
613 leak_block_get_selected (ProcessWindow *pwin)
615 GtkTreeSelection *selection;
616 GtkTreeModel *model;
617 GtkTreeIter iter;
618 Block *block = NULL;
620 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pwin->leak_block_tree_view));
621 if (selection && gtk_tree_selection_get_selected (selection, &model, &iter))
622 gtk_tree_model_get (model, &iter, LEAK_BLOCK_BLOCK, &block, -1);
624 return block;
627 static void
628 leak_block_selection_changed (GtkTreeSelection *selection,
629 ProcessWindow *pwin)
631 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
632 GtkListStore *store = GTK_LIST_STORE (model);
633 Block *block = leak_block_get_selected (pwin);
634 StackNode *stack;
636 gtk_list_store_clear (store);
638 if (block == NULL)
639 return;
641 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
643 for (stack = block->stack; stack != NULL; stack = stack->parent) {
644 GtkTreeIter iter;
645 const char *filename;
646 char *functionname;
647 unsigned int line;
649 if (!process_find_line (pwin->process, stack->address,
650 &filename, &functionname, &line)) {
651 /* 0x3f == '?' -- suppress trigraph warnings */
652 functionname = g_strdup ("(\x3f\x3f\x3f)");
653 filename = "(\x3f\x3f\x3f)";
654 line = 0;
657 gtk_list_store_append (store, &iter);
658 gtk_list_store_set (store, &iter,
659 LEAK_STACK_NAME, functionname,
660 LEAK_STACK_LINE, line,
661 LEAK_STACK_FILE, filename,
662 -1);
665 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
668 static void
669 leaks_fill (ProcessWindow *pwin)
671 GSList *tmp_list;
672 GtkTreeModel *model;
673 GtkListStore *store;
675 model = gtk_tree_view_get_model (GTK_TREE_VIEW (pwin->leak_block_tree_view));
676 store = GTK_LIST_STORE (model);
678 gtk_list_store_clear (store);
680 tree_view_unset_sort_ids (GTK_TREE_VIEW (pwin->leak_block_tree_view));
682 tmp_list = pwin->leaks;
683 while (tmp_list) {
684 const char *filename;
685 char *functionname = NULL;
686 GtkTreeIter iter;
688 unsigned int line;
690 Block *block = tmp_list->data;
691 StackNode *stack;
693 for (stack = block->stack; stack != NULL; stack = stack->parent) {
694 if (process_find_line (pwin->process, stack->address,
695 &filename, &functionname, &line)) {
696 GSList *tmp_list;
698 if (!functionname)
699 continue;
701 for (tmp_list = skip_funcs; tmp_list != NULL; tmp_list = tmp_list->next) {
702 if (!strcmp (functionname, tmp_list->data)) {
703 free (functionname);
704 functionname = NULL;
705 break;
709 if (!functionname)
710 continue;
712 for (tmp_list = skip_regexes; tmp_list != NULL; tmp_list = tmp_list->next) {
713 regex_t regex;
715 regcomp (&regex, tmp_list->data, 0);
717 if (!regexec (&regex, functionname, 0, NULL, 0)) {
718 free (functionname);
719 functionname = NULL;
720 regfree (&regex);
721 break;
724 regfree (&regex);
728 if (functionname)
729 break;
731 if (!functionname)
732 functionname = g_strdup ("(\x3f\x3f\x3f)");
734 gtk_list_store_append (store, &iter);
735 gtk_list_store_set (store, &iter,
736 LEAK_BLOCK_ADDR, block->addr,
737 LEAK_BLOCK_SIZE, block->size,
738 LEAK_BLOCK_CALLER, functionname,
739 LEAK_BLOCK_BLOCK, block,
740 -1);
742 g_free (functionname);
744 tmp_list = tmp_list->next;
747 tree_view_set_sort_ids (GTK_TREE_VIEW (pwin->leak_block_tree_view));
750 static void
751 leak_stack_run_command (ProcessWindow *pwin, Block *block, int frame)
753 const char *filename;
754 char *functionname;
755 unsigned int line;
757 StackNode *stack = block->stack;
758 while (frame--)
759 stack = stack->parent;
761 if (process_find_line (pwin->process, stack->address,
762 &filename, &functionname, &line)) {
764 GString *command = g_string_new (NULL);
765 char *p = stack_command;
766 char buf[32];
767 char *cmdline;
768 GError *err = NULL;
770 while (*p) {
771 if (*p == '%') {
772 switch (*++p) {
773 case 'f':
774 g_string_append (command, filename);
775 break;
776 case 'l':
777 snprintf(buf, 32, "%d", line);
778 g_string_append (command, buf);
779 break;
780 case '%':
781 g_string_append_c (command, '%');
782 break;
783 default:
784 g_string_append_c (command, '%');
785 g_string_append_c (command, *p);
787 } else
788 g_string_append_c (command, *p);
789 p++;
792 free (functionname);
794 cmdline = g_strdup_printf ("/bin/sh -c %s", command->str);
796 if (!g_spawn_command_line_async (cmdline, &err)) {
797 show_error (pwin->main_window,
798 ERROR_MODAL, _("Executation of \"%s\" failed: %s"),
799 command->str, err->message);
801 g_error_free (err);
804 g_free (cmdline);
806 g_string_free (command, FALSE);
810 static void
811 leak_stack_row_activated (GtkTreeView *tree_view,
812 GtkTreePath *path,
813 int column,
814 ProcessWindow *pwin)
816 GtkTreeModel *model;
817 GtkTreeIter iter;
818 int frame;
819 Block *block;
821 model = gtk_tree_view_get_model (tree_view);
822 gtk_tree_model_get_iter (model, &iter, path);
823 frame = list_iter_get_index (model, &iter);
825 block = leak_block_get_selected (pwin);
826 if (block)
827 leak_stack_run_command (pwin, block, frame);
832 /************************************************************
833 * File Selection handling
834 ************************************************************/
836 static void
837 filename_ok_clicked (GtkWidget *button, gchar **name)
839 GtkWidget *fs = gtk_widget_get_ancestor (button,
840 gtk_file_selection_get_type());
842 *name = g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
843 gtk_widget_destroy (fs);
846 static gchar *
847 get_filename (const gchar *title,
848 const gchar *prompt_text,
849 const gchar *suggested_name)
851 GtkWidget *fs;
852 gchar *filename = NULL;
854 fs = gtk_file_selection_new (title);
855 gtk_file_selection_set_filename (GTK_FILE_SELECTION (fs),
856 suggested_name);
858 gtk_label_set_text (GTK_LABEL (GTK_FILE_SELECTION (fs)->selection_text),
859 prompt_text);
860 g_signal_connect (GTK_FILE_SELECTION (fs)->ok_button, "clicked",
861 G_CALLBACK (filename_ok_clicked), &filename);
862 g_signal_connect_swapped (GTK_FILE_SELECTION (fs)->cancel_button, "clicked",
863 G_CALLBACK (gtk_widget_destroy), fs);
864 g_signal_connect (fs, "destroy",
865 G_CALLBACK (gtk_main_quit), NULL);
867 gtk_widget_show (fs);
868 gtk_main();
870 return filename;
873 /* Really ugly utility function to retrieve the ProcessWindow from
874 * either a menu_item or toolbar item.
876 ProcessWindow *
877 pwin_from_widget (GtkWidget *widget)
879 GtkWidget *app;
881 if (GTK_IS_MENU_ITEM (widget)) {
882 GtkWidget *menu_shell = widget->parent;
884 while (menu_shell && !GTK_IS_MENU_BAR (menu_shell)) {
885 menu_shell = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))->parent;
887 g_assert (menu_shell != NULL);
889 app = gtk_widget_get_toplevel (menu_shell);
890 } else
891 app = gtk_widget_get_toplevel (widget);
893 return g_object_get_data (G_OBJECT (app), "process-window");
896 void
897 close_cb (GtkWidget *widget)
899 ProcessWindow *pwin = pwin_from_widget (widget);
901 hide_and_check_quit (pwin->main_window);
904 void
905 exit_cb (GtkWidget *widget)
907 gtk_main_quit ();
910 static void
911 reset_cb (MPProcess *process, ProcessWindow *pwin)
913 process_window_reset (pwin);
916 static void
917 status_changed_cb (MPProcess *process, ProcessWindow *pwin)
919 if (process->status == MP_PROCESS_DEFUNCT ||
920 process->status == MP_PROCESS_DETACHED) {
922 if (g_slist_length (process_windows) > 1) {
923 tree_window_remove (pwin);
924 process_window_destroy (pwin);
925 } else {
926 tree_window_remove (pwin);
928 if (pwin->process) {
929 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin->process), G_CALLBACK (status_changed_cb), pwin);
930 g_signal_handlers_disconnect_by_func (G_OBJECT (pwin->process), G_CALLBACK (reset_cb), pwin);
931 g_object_unref (G_OBJECT (pwin->process));
932 pwin->process = NULL;
935 if (pwin->status_update_timeout) {
936 g_source_remove (pwin->status_update_timeout);
937 pwin->status_update_timeout = 0;
940 process_window_reset (pwin);
943 } else {
944 char *status = process_get_status_text (process);
945 char *cmdline = process_get_cmdline (process);
946 char *title = g_strdup_printf ("%s - %s (%d) - %s", _("MemProf"), cmdline, process->pid, status);
947 gtk_window_set_title (GTK_WINDOW (pwin->main_window), title);
949 g_free (title);
950 g_free (status);
951 g_free (cmdline);
955 static void
956 list_view_clear (GtkTreeView *tree_view)
958 GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
959 gtk_list_store_clear (GTK_LIST_STORE (model));
962 static void
963 process_window_reset (ProcessWindow *pwin)
965 if (pwin->profile) {
966 profile_free (pwin->profile);
967 pwin->profile = NULL;
969 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_func_tree_view), NULL);
970 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_caller_tree_view), NULL);
971 gtk_tree_view_set_model (GTK_TREE_VIEW (pwin->profile_descendants_tree_view), NULL);
973 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_func_tree_view), FALSE);
974 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_caller_tree_view), FALSE);
975 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), FALSE);
978 if (pwin->leaks) {
979 g_slist_free (pwin->leaks);
980 pwin->leaks = NULL;
981 list_view_clear (GTK_TREE_VIEW (pwin->leak_block_tree_view));
982 list_view_clear (GTK_TREE_VIEW (pwin->leak_stack_tree_view));
985 pwin->usage_max = 32*1024;
986 pwin->usage_high = 0;
987 pwin->usage_leaked = 0;
989 gtk_window_set_title (GTK_WINDOW (pwin->main_window), "MemProf");
991 gtk_widget_queue_draw (pwin->usage_area);
994 static void
995 init_process (ProcessWindow *pwin, MPProcess *process)
997 pwin->process = process;
999 process_set_follow_fork (pwin->process, default_follow_fork);
1000 process_set_follow_exec (pwin->process, default_follow_exec);
1002 pwin->status_update_timeout =
1003 g_timeout_add (100,
1004 update_status,
1005 pwin);
1007 g_signal_connect (process, "status_changed",
1008 G_CALLBACK (status_changed_cb), pwin);
1009 g_signal_connect (process, "reset",
1010 G_CALLBACK (reset_cb), pwin);
1012 tree_window_add (pwin);
1015 static void
1016 process_created_cb (MPServer *server, MPProcess *process)
1018 ProcessWindow *pwin = process_window_new ();
1020 init_process (pwin, process);
1022 tree_window_show ();
1025 static gboolean
1026 run_file (ProcessWindow *pwin, char **args)
1028 gboolean result;
1029 char *path;
1031 g_return_val_if_fail (args != NULL, FALSE);
1032 g_return_val_if_fail (args[0] != NULL, FALSE);
1034 path = process_find_exec (args);
1036 if (path) {
1037 g_warning ("Process new '%s'\n", path);
1038 MPProcess *process = process_new (global_server);
1039 process_run (process, path, args);
1041 if (pwin->process) {
1042 pwin = process_window_new ();
1043 tree_window_show ();
1046 init_process (pwin, process);
1048 gtk_widget_show (pwin->main_window);
1050 result = TRUE;
1052 } else {
1053 show_error (pwin->main_window,
1054 ERROR_MODAL,
1055 _("Cannot find executable for \"%s\""),
1056 args[0]);
1057 result = FALSE;
1060 g_free (path);
1061 return result;
1065 void
1066 run_cb (GtkWidget *widget)
1068 GladeXML *xml;
1069 GtkWidget *run_dialog;
1070 GtkWidget *entry;
1072 ProcessWindow *pwin = pwin_from_widget (widget);
1074 xml = glade_xml_new (glade_file, "RunDialog", NULL);
1075 run_dialog = get_widget (xml, "RunDialog");
1076 entry = get_widget (xml, "RunDialog-entry");
1078 g_signal_connect_swapped (entry, "activate",
1079 G_CALLBACK (gtk_widget_activate),
1080 get_widget (xml, "RunDialog-run"));
1082 g_object_unref (G_OBJECT (xml));
1084 while (1) {
1085 gtk_window_set_transient_for (GTK_WINDOW (run_dialog),
1086 GTK_WINDOW (pwin->main_window));
1087 if (gtk_dialog_run (GTK_DIALOG (run_dialog)) == 1) {
1088 gchar **args;
1089 char *text;
1090 gboolean result;
1092 text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
1093 args = process_parse_exec (text);
1095 result = run_file (pwin, args);
1097 g_strfreev (args);
1098 g_free (text);
1100 if (result)
1101 break;
1102 } else {
1103 break;
1107 gtk_widget_destroy (run_dialog);
1110 void
1111 kill_cb (GtkWidget *widget)
1113 ProcessWindow *pwin = pwin_from_widget (widget);
1115 if (pwin->process)
1116 process_window_maybe_kill (pwin);
1119 void
1120 detach_cb (GtkWidget *widget)
1122 ProcessWindow *pwin = pwin_from_widget (widget);
1124 if (pwin->process)
1125 process_window_maybe_detach (pwin);
1128 void
1129 process_tree_cb (GtkWidget *widget)
1131 tree_window_show ();
1134 void
1135 save_leak_cb (GtkWidget *widget)
1137 static gchar *suggestion = NULL;
1138 gchar *filename;
1140 ProcessWindow *pwin = pwin_from_widget (widget);
1142 if (pwin->leaks) {
1143 filename = get_filename ("Save Leak Report", "Output file",
1144 suggestion ? suggestion : "memprof.leak");
1145 if (filename) {
1146 g_free (suggestion);
1147 suggestion = filename;
1149 leaks_print (pwin->process, pwin->leaks, filename);
1154 void
1155 save_profile_cb (GtkWidget *widget)
1157 static gchar *suggestion = NULL;
1158 gchar *filename;
1160 ProcessWindow *pwin = pwin_from_widget (widget);
1162 if (pwin->profile) {
1163 filename = get_filename ("Save Profile", "Output file",
1164 suggestion ? suggestion : "memprof.out");
1165 if (filename) {
1166 g_free (suggestion);
1167 suggestion = filename;
1169 profile_write (pwin->profile, filename);
1174 void
1175 save_current_cb (GtkWidget *widget)
1177 ProcessWindow *pwin = pwin_from_widget (widget);
1179 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (pwin->main_notebook))) {
1180 case 0:
1181 save_profile_cb (widget);
1182 break;
1183 case 1:
1184 save_leak_cb (widget);
1185 break;
1189 void
1190 generate_leak_cb (GtkWidget *widget)
1192 GSList *tmp_list;
1193 ProcessWindow *pwin = pwin_from_widget (widget);
1196 if (pwin->process) {
1197 process_stop_input (pwin->process);
1199 while (pwin->leaks) {
1200 block_unref(pwin->leaks->data);
1201 pwin->leaks = g_slist_delete_link(pwin->leaks, pwin->leaks);
1203 pwin->leaks = leaks_find (pwin->process);
1205 pwin->usage_leaked = 0;
1206 tmp_list = pwin->leaks;
1207 while (tmp_list) {
1208 pwin->usage_leaked += ((Block *)tmp_list->data)->size;
1209 tmp_list = tmp_list->next;
1212 leaks_fill (pwin);
1213 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 1);
1215 process_start_input (pwin->process);
1219 void
1220 generate_profile_cb (GtkWidget *widget)
1222 ProcessWindow *pwin = pwin_from_widget (widget);
1224 if (pwin->process) {
1225 process_stop_input (pwin->process);
1227 if (pwin->profile) {
1228 profile_free (pwin->profile);
1229 pwin->profile = NULL;
1232 pwin->profile = profile_create (pwin->process, skip_funcs);
1233 process_start_input (pwin->process);
1234 profile_fill (pwin);
1236 gtk_notebook_set_current_page (GTK_NOTEBOOK (pwin->main_notebook), 0);
1240 void
1241 reset_profile_cb (GtkWidget *widget)
1243 ProcessWindow *pwin = pwin_from_widget (widget);
1245 process_window_reset (pwin);
1247 if (pwin->process)
1248 process_clear_input (pwin->process);
1251 void
1252 record_button_toggled_cb (GtkWidget *widget)
1254 ProcessWindow *pwin = pwin_from_widget (widget);
1256 if (gtk_toggle_tool_button_get_active
1257 (GTK_TOGGLE_TOOL_BUTTON (widget)))
1258 process_start_input (pwin->process);
1259 else
1260 process_stop_input (pwin->process);
1263 void
1264 about_cb (GtkWidget *widget)
1266 #define OSLASH "\303\270"
1267 ProcessWindow *pwin = pwin_from_widget (widget);
1269 /* FIXME: restore credits */
1270 gtk_show_about_dialog (GTK_WINDOW (pwin->main_window),
1271 #if 0
1272 "logo", pwin->icon,
1273 #endif
1274 "name", "MemProf",
1275 "copyright",
1276 "Copyright 1999, 2000, 2001, Red Hat, Inc.\n"
1277 "Copyright 2002, Kristian Rietveld\n"
1278 "Copyright 2002, 2006, 2007, S"OSLASH"ren Sandmann",
1279 #if 0
1280 "copyright", "Copyright 2004-2006, S"OSLASH"ren Sandmann",
1281 #endif
1282 "version", PACKAGE_VERSION,
1283 NULL);
1286 static void
1287 show_error_response (GtkDialog *dialog,
1288 gint response_id,
1289 gpointer user_data)
1291 if (response_id == GTK_RESPONSE_OK)
1292 gtk_widget_destroy (GTK_WIDGET (dialog));
1295 void
1296 show_error (GtkWidget *parent_window,
1297 ErrorType error,
1298 const gchar *format,
1299 ...)
1301 va_list args;
1302 char *message;
1303 GtkWidget *dialog;
1305 va_start (args, format);
1306 g_vasprintf (&message, format, args);
1307 va_end (args);
1309 dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL,
1310 GTK_DIALOG_DESTROY_WITH_PARENT,
1311 (error == ERROR_FATAL) ?
1312 GTK_MESSAGE_ERROR :
1313 GTK_MESSAGE_WARNING,
1314 GTK_BUTTONS_OK, "%s", message);
1315 g_free (message);
1317 gtk_window_set_title (GTK_WINDOW (dialog),
1318 (error == ERROR_FATAL) ?
1319 _("MemProf Error") : _("MemProf Warning"));
1321 if (error == ERROR_WARNING) {
1322 gtk_widget_show (dialog);
1323 g_signal_connect (dialog, "response",
1324 G_CALLBACK (show_error_response), NULL);
1325 } else {
1326 gtk_dialog_run (GTK_DIALOG (dialog));
1327 gtk_widget_destroy (dialog);
1330 if (error == ERROR_FATAL)
1331 exit(1);
1334 static void
1335 process_window_free (ProcessWindow *pwin)
1337 /* FIXME: we leak the process structure */
1339 if (pwin->leaks)
1340 g_slist_free (pwin->leaks);
1342 if (pwin->profile)
1343 profile_free (pwin->profile);
1345 process_windows = g_slist_remove (process_windows, pwin);
1346 if (!process_windows)
1347 gtk_main_quit ();
1349 g_free (pwin);
1353 static void
1354 process_window_destroy (ProcessWindow *pwin)
1356 if (pwin->status_update_timeout)
1357 g_source_remove (pwin->status_update_timeout);
1359 gtk_widget_destroy (pwin->main_window);
1360 check_quit ();
1363 static GtkTreeViewColumn *
1364 add_sample_column (GtkTreeView *view, const gchar *title, gint model_column)
1366 const char *format;
1368 if (profile_type == MP_PROFILE_MEMORY)
1369 format = "%.0f";
1370 else
1371 format = "%.2f";
1373 return add_double_format_column (view, title, model_column, format);
1376 static void
1377 setup_profile_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1379 GtkTreeSelection *selection;
1380 GtkTreeViewColumn *col;
1382 col = add_plain_text_column (tree_view, _("Functions"), PROFILE_FUNC_NAME);
1383 add_sample_column (tree_view, _("Self"), PROFILE_FUNC_SELF);
1384 add_sample_column (tree_view, _("Total"), PROFILE_FUNC_TOTAL);
1385 gtk_tree_view_column_set_expand (col, TRUE);
1387 selection = gtk_tree_view_get_selection (tree_view);
1388 g_return_if_fail (selection != NULL);
1389 g_signal_connect (selection, "changed", G_CALLBACK (profile_selection_changed), pwin);
1391 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1394 static void
1395 setup_profile_descendants_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1397 GtkTreeViewColumn *col;
1399 col = add_plain_text_column (tree_view, _("Descendants"), PROFILE_DESCENDANTS_NAME);
1400 add_sample_column (tree_view, _("Self"), PROFILE_DESCENDANTS_SELF);
1401 add_sample_column (tree_view, _("Cumulative"), PROFILE_DESCENDANTS_NONRECURSE);
1402 gtk_tree_view_column_set_expand (col, TRUE);
1404 gtk_widget_set_sensitive (GTK_WIDGET (pwin->profile_descendants_tree_view), FALSE);
1406 g_signal_connect (tree_view, "row-activated",
1407 G_CALLBACK (profile_descendants_row_activated), pwin);
1409 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1412 static void
1413 setup_profile_caller_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1415 GtkTreeViewColumn *col;
1417 col = add_plain_text_column (tree_view, _("Callers"), PROFILE_CALLER_NAME);
1418 add_sample_column (tree_view, _("Self"), PROFILE_CALLER_SELF);
1419 add_sample_column (tree_view, _("Total"), PROFILE_CALLER_TOTAL);
1420 gtk_tree_view_column_set_expand (col, TRUE);
1422 g_signal_connect (tree_view, "row-activated",
1423 G_CALLBACK (profile_caller_row_activated), pwin);
1425 gtk_widget_set_sensitive (GTK_WIDGET (tree_view), FALSE);
1428 static void
1429 setup_leak_block_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1431 GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
1432 GtkListStore *store;
1434 g_return_if_fail (selection != NULL);
1436 store = gtk_list_store_new (4,
1437 G_TYPE_POINTER,
1438 G_TYPE_INT,
1439 G_TYPE_STRING,
1440 G_TYPE_POINTER);
1442 gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
1444 add_pointer_column (tree_view, _("Address"), LEAK_BLOCK_ADDR);
1445 add_plain_text_column (tree_view, _("Size"), LEAK_BLOCK_SIZE);
1446 add_plain_text_column (tree_view, _("Caller"), LEAK_BLOCK_CALLER);
1448 g_signal_connect (selection, "changed",
1449 G_CALLBACK (leak_block_selection_changed), pwin);
1451 gtk_tree_view_columns_autosize (tree_view);
1454 static void
1455 setup_leak_stack_tree_view (ProcessWindow *pwin, GtkTreeView *tree_view)
1457 GtkListStore *store;
1459 store = gtk_list_store_new (3,
1460 G_TYPE_STRING,
1461 G_TYPE_INT,
1462 G_TYPE_STRING);
1464 gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
1466 add_plain_text_column (tree_view, _("Function"), LEAK_STACK_NAME);
1467 add_plain_text_column (tree_view, _("Line"), LEAK_STACK_LINE);
1468 add_plain_text_column (tree_view, _("File"), LEAK_STACK_FILE);
1470 g_signal_connect (tree_view, "row-activated",
1471 G_CALLBACK (leak_stack_row_activated), pwin);
1473 gtk_tree_view_columns_autosize (tree_view);
1476 static void
1477 set_default_size (GtkWindow *window)
1479 GdkScreen *screen;
1480 int monitor_num;
1481 GdkRectangle monitor;
1482 int width, height;
1483 GtkWidget *widget = GTK_WIDGET (window);
1485 screen = gtk_widget_get_screen (widget);
1486 monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
1488 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1490 width = monitor.width * 3 / 4;
1491 height = monitor.height * 3 / 4;
1493 gtk_window_resize (window, width, height);
1496 static ProcessWindow *
1497 process_window_new (void)
1499 gchar *fullfilename;
1500 GladeXML *xml;
1501 GtkWidget *vpaned;
1502 GtkWidget *hpaned;
1503 ProcessWindow *pwin;
1504 GError *err = NULL;
1506 pwin = g_new0 (ProcessWindow, 1);
1507 process_windows = g_slist_prepend (process_windows, pwin);
1509 pwin->process = NULL;
1510 pwin->profile = NULL;
1511 pwin->leaks = NULL;
1513 pwin->usage_max = 32*1024;
1514 pwin->usage_high = 0;
1515 pwin->usage_leaked = 0;
1517 xml = glade_xml_new (glade_file, "MainWindow", NULL);
1519 pwin->main_window = get_widget (xml, "MainWindow");
1520 gtk_widget_realize (pwin->main_window);
1522 fullfilename = g_strdup ("./memprof.png");
1524 if (!g_file_test (fullfilename, G_FILE_TEST_EXISTS))
1525 fullfilename = g_build_filename (DATADIR, "memprof.png", NULL);
1528 gtk_window_set_icon_from_file (GTK_WINDOW (pwin->main_window), fullfilename, &err);
1530 g_free (fullfilename);
1532 g_signal_connect (pwin->main_window, "delete_event",
1533 G_CALLBACK (hide_and_check_quit), pwin);
1536 set_default_size (GTK_WINDOW (pwin->main_window));
1538 g_object_set_data_full (G_OBJECT (pwin->main_window),
1539 "process-window",
1540 pwin, (GDestroyNotify)process_window_free);
1542 pwin->main_notebook = get_widget (xml, "main-notebook");
1544 pwin->n_allocations_label = get_widget (xml, "n-allocations-label");
1545 pwin->bytes_per_label = get_widget (xml, "bytes-per-label");
1546 pwin->total_bytes_label = get_widget (xml, "total-bytes-label");
1548 pwin->profile_status_label = get_widget (xml, "profile-status-label");
1550 /* setup profile tree views */
1551 pwin->profile_func_tree_view = get_widget (xml, "profile-func-tree-view");
1552 pwin->profile_descendants_tree_view =
1553 get_widget (xml, "profile-descendants-tree-view");
1554 pwin->profile_caller_tree_view = get_widget (xml, "profile-caller-tree-view");
1556 setup_profile_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_func_tree_view));
1557 setup_profile_descendants_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_descendants_tree_view));
1558 setup_profile_caller_tree_view (pwin, GTK_TREE_VIEW (pwin->profile_caller_tree_view));
1560 /* leak tree views */
1561 pwin->leak_block_tree_view = get_widget (xml, "leak-block-tree-view");
1562 pwin->leak_stack_tree_view = get_widget (xml, "leak-stack-tree-view");
1564 setup_leak_block_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_block_tree_view));
1565 setup_leak_stack_tree_view (pwin, GTK_TREE_VIEW (pwin->leak_stack_tree_view));
1567 pwin->usage_max_label = get_widget (xml, "usage-max-label");
1568 pwin->usage_area = get_widget (xml, "usage-area");
1570 g_signal_connect (pwin->usage_area, "expose_event",
1571 G_CALLBACK (on_usage_area_expose), pwin);
1573 vpaned = get_widget (xml, "profile-vpaned");
1574 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1576 hpaned = get_widget (xml, "profile-hpaned");
1577 gtk_paned_set_position (GTK_PANED (hpaned), 150);
1579 vpaned = get_widget (xml, "leaks-vpaned");
1580 gtk_paned_set_position (GTK_PANED (vpaned), 150);
1582 /* If profiling time, not memory, hide all GUI related to leak
1583 * detection.
1585 if (profile_type != MP_PROFILE_MEMORY) {
1586 gtk_widget_hide (get_widget (xml, "leaks-vpaned"));
1587 gtk_widget_hide (get_widget (xml, "toolbar-leaks-button"));
1588 gtk_widget_hide (get_widget (xml, "save-leak-info"));
1589 gtk_widget_hide (get_widget (xml, "generate-leak-report"));
1590 gtk_widget_hide (get_widget (xml, "allocation-bar"));
1591 gtk_notebook_set_show_tabs (
1592 GTK_NOTEBOOK (get_widget (xml, "main-notebook")), FALSE);
1594 else {
1595 gtk_widget_hide (get_widget (xml, "profile-status-label"));
1596 gtk_widget_hide (get_widget (xml, "reset-profile-button"));
1599 glade_xml_signal_autoconnect (xml);
1600 g_object_unref (G_OBJECT (xml));
1602 return pwin;
1606 MPProcess *
1607 process_window_get_process (ProcessWindow *pwin)
1609 return pwin->process;
1612 gboolean
1613 process_window_visible (ProcessWindow *pwin)
1615 return GTK_WIDGET_VISIBLE (pwin->main_window);
1618 void
1619 process_window_show (ProcessWindow *pwin)
1621 if (!process_window_visible (pwin))
1622 gtk_widget_show (pwin->main_window);
1623 else
1624 gdk_window_show (pwin->main_window->window);
1627 void
1628 process_window_hide (ProcessWindow *pwin)
1630 if (process_window_visible (pwin))
1631 hide_and_check_quit (pwin->main_window);
1634 void
1635 check_quit (void)
1637 GList *toplevels, *tmplist;
1639 tmplist = toplevels = gtk_window_list_toplevels ();
1640 while (tmplist) {
1641 if (GTK_WIDGET_VISIBLE (toplevels->data))
1642 return;
1643 tmplist = tmplist->next;
1646 g_list_free (toplevels);
1648 gtk_main_quit ();
1651 gboolean
1652 hide_and_check_quit (GtkWidget *window)
1654 gtk_widget_hide (window);
1655 check_quit ();
1657 return TRUE;
1661 void
1662 process_window_maybe_detach (ProcessWindow *pwin)
1664 GtkWidget *dialog;
1665 const char *message;
1666 gint response;
1668 if (pwin->process->status == MP_PROCESS_EXITING)
1669 message = _("Really detach from finished process?");
1670 else
1671 message = _("Really detach from running process?");
1673 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1674 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1675 GTK_MESSAGE_QUESTION,
1676 GTK_BUTTONS_YES_NO,
1677 "%s", message);
1678 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1680 response = gtk_dialog_run (GTK_DIALOG (dialog));
1681 gtk_widget_destroy (dialog);
1683 if (response == GTK_RESPONSE_YES)
1684 process_detach (pwin->process);
1688 void
1689 process_window_maybe_kill (ProcessWindow *pwin)
1691 if (pwin->process->status == MP_PROCESS_EXITING)
1692 process_window_maybe_detach (pwin);
1693 else {
1694 GtkWidget *dialog;
1695 gint response;
1697 dialog = gtk_message_dialog_new (GTK_WINDOW (pwin->main_window),
1698 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1699 GTK_MESSAGE_QUESTION,
1700 GTK_BUTTONS_YES_NO,
1701 _("Really kill running process?"));
1702 gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1703 GTK_RESPONSE_YES);
1705 response = gtk_dialog_run (GTK_DIALOG (dialog));
1706 gtk_widget_destroy (dialog);
1708 if (response == GTK_RESPONSE_YES)
1709 process_kill (pwin->process);
1713 void
1714 sigchld_handler (int signum)
1716 int old_errno = errno;
1718 while (1) {
1719 int pid = waitpid (WAIT_ANY, NULL, WNOHANG);
1720 if (pid < 0 && errno != ECHILD)
1721 g_error ("waitpid: %s\n", g_strerror (errno));
1722 else if (pid <= 0)
1723 break;
1726 errno = old_errno;
1729 static char *profile_type_string = NULL;
1730 static char *profile_rate_string = NULL;
1731 static int profile_interval = 1000;
1733 static const GOptionEntry entries[] =
1735 { "follow-fork", '\0', 0, G_OPTION_ARG_NONE, &default_follow_fork,
1736 N_("Create new windows for forked processes"), NULL },
1737 { "follow-exec", '\0', 0, G_OPTION_ARG_NONE, &default_follow_exec,
1738 N_("Retain windows for processes after exec()"), NULL },
1739 { "profile", '\0', 0, G_OPTION_ARG_STRING, &profile_type_string,
1740 N_("Type of profiling information to collect"), "memory/cycles/time" },
1741 { "rate", '\0', 0, G_OPTION_ARG_STRING, &profile_rate_string,
1742 N_("Number of samples/sec for time profile (1k=1000)"), NULL },
1743 { NULL }
1746 static void
1747 parse_options (int *argc, char ***argv)
1749 GError *err = NULL;
1750 GOptionContext *context;
1752 context = g_option_context_new ("- A memory profiler");
1754 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
1755 g_option_context_add_group (context, gtk_get_option_group (TRUE));
1756 g_option_context_parse (context, argc, argv, &err);
1760 main(int argc, char **argv)
1762 ProcessWindow *initial_window;
1764 gtk_init (&argc, &argv);
1766 parse_options (&argc, &argv);
1768 /* Set up a handler for SIGCHLD to avoid zombie children
1770 signal (SIGCHLD, sigchld_handler);
1772 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1773 textdomain (GETTEXT_PACKAGE);
1775 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1776 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1777 #endif
1779 /* If the user didn't specify the profile type explicitely,
1780 * we guess from the executable name.
1782 if (!profile_type_string) {
1783 char *basename;
1784 basename = g_path_get_basename (argv[0]);
1786 if (strcmp (basename, "speedprof") == 0)
1787 profile_type_string = "cycles";
1788 else
1789 profile_type_string = "memory";
1791 g_free (basename);
1794 if (strcmp (profile_type_string, "memory") == 0)
1795 profile_type = MP_PROFILE_MEMORY;
1796 else if (strcmp (profile_type_string, "cycles") == 0)
1797 profile_type = MP_PROFILE_CYCLES;
1798 else if (strcmp (profile_type_string, "time") == 0)
1799 profile_type = MP_PROFILE_TIME;
1800 else {
1801 g_printerr (_("Argument of --profile must be one of 'memory', 'cycles', or 'time'\n"));
1802 exit (1);
1805 if (profile_rate_string) {
1806 int multiplier = 1;
1807 double rate;
1808 int len = strlen (profile_rate_string);
1809 char suffix[2] = { '\0', '\0' };
1810 char *end;
1812 if (len > 0 &&
1813 (profile_rate_string[len - 1] == 'k' ||
1814 profile_rate_string[len - 1] == 'K')) {
1815 suffix[0] = profile_rate_string[len - 1];
1816 multiplier = 1000;
1817 profile_rate_string[len - 1] = '\0';
1820 rate = strtod (profile_rate_string, &end);
1821 if (len == 0 || *end != '\0' ||
1822 rate * multiplier <= 1 || rate * multiplier > 1000000) {
1823 g_printerr ("Invalid rate: %s%s\n",
1824 profile_rate_string, suffix);
1825 exit (1);
1828 profile_interval = (int) (0.5 + 1000000 / (rate * multiplier));
1831 glade_file = "./memprof.glade";
1832 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1833 glade_file = g_build_filename (DATADIR, "memprof.glade", NULL);
1835 if (!g_file_test (glade_file, G_FILE_TEST_EXISTS)) {
1836 show_error (NULL, ERROR_FATAL, _("Cannot find memprof.glade"));
1839 global_server = mp_server_new ();
1840 mp_server_set_profile_type (global_server, profile_type);
1841 mp_server_set_interval (global_server, profile_interval);
1843 g_signal_connect (global_server, "process_created",
1844 G_CALLBACK (process_created_cb), NULL);
1846 initial_window = process_window_new ();
1848 gtk_widget_show (initial_window->main_window);
1850 if (argc > 1)
1851 run_file (initial_window, (char **)(argv + 1));
1853 gtk_main ();
1855 return 0;