Update autogen.sh and comment out --enable-compile-warnings
[memprof.git] / process.c
blobe5ef5d38aa0de3f3f187e736168099505271964e
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
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 /*====*/
23 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 #include <sys/sysmacros.h>
36 #include <glib-object.h>
38 #include "memintercept.h"
39 #include "memprof.h"
40 #include "process.h"
41 #include "server.h"
42 #include <glib/gi18n.h>
44 enum {
45 STATUS_CHANGED,
46 RESET,
47 LAST_SIGNAL
49 static guint process_signals[LAST_SIGNAL] = { 0 };
51 static void mp_process_class_init (MPProcessClass *class);
52 static void mp_process_init (MPProcess *process);
53 static void mp_process_finalize (GObject *object);
54 static void process_reinit (MPProcess *process);
56 #define MP_PAGE_SIZE 4096
58 /* Code to keep a queue of commands read
60 typedef struct {
61 MIInfo info;
62 StackNode *stack;
63 } Command;
65 static gint
66 queue_compare (gconstpointer a, gconstpointer b)
68 const Command *commanda = a;
69 const Command *commandb = b;
71 return (commanda->info.any.seqno < commandb->info.any.seqno ? -1 :
72 (commanda->info.any.seqno > commandb->info.any.seqno ? 1 : 0));
75 static void
76 queue_command (MPProcess *process, MIInfo *info, StackNode *stack)
78 Command *command = g_new (Command, 1);
79 command->info = *info;
80 command->stack = stack;
82 process->command_queue = g_list_insert_sorted (process->command_queue, command, queue_compare);
85 static gboolean
86 unqueue_command (MPProcess *process, MIInfo *info, StackNode **stack)
88 Command *command = process->command_queue->data;
89 GList *tmp_list;
91 if (command->info.any.seqno == process->seqno) {
92 *stack = command->stack;
93 *info = command->info;
95 tmp_list = process->command_queue;
96 process->command_queue = g_list_remove_link (process->command_queue, tmp_list);
97 g_free (command);
98 g_list_free_1 (tmp_list);
100 return TRUE;
101 } else
102 return FALSE;
105 /************************************************************
106 * Block Manipulation
107 ************************************************************/
109 void
110 block_unref (Block *block)
112 block->refcount--;
113 if (block->refcount == 0)
114 g_free (block);
117 /************************************************************
118 * Code to map addresses to object files
119 ************************************************************/
120 typedef struct {
121 guint addr;
122 guint size;
123 gchar *name;
124 } Symbol;
126 static gint
127 compare_address (const void *symbol1, const void *symbol2)
129 return (((Symbol *)symbol1)->addr < ((Symbol *)symbol2)->addr) ?
130 -1 : ((((Symbol *)symbol1)->addr == ((Symbol *)symbol2)->addr) ?
131 0 : 1);
134 void
135 prepare_map (Map *map)
137 g_return_if_fail (!map->prepared);
139 map->binfile = bin_file_new (map->name);
141 map->prepared = TRUE;
144 static void
145 process_read_maps (MPProcess *process)
147 gchar buffer[1024];
148 FILE *in;
150 snprintf (buffer, 1023, "/proc/%d/maps", process->pid);
152 in = fopen (buffer, "r");
154 if (process->map_list) {
155 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
156 g_list_free (process->map_list);
158 process->map_list = NULL;
161 while (fgets(buffer, 1023, in)) {
162 char file[256];
163 int count;
164 gulong start;
165 gulong end;
166 gulong offset;
167 gulong inode;
169 count = sscanf (
170 buffer, "%lx-%lx %*15s %lx %*x:%*x %lu %255s",
171 &start, &end, &offset, &inode, file);
172 if (count == 5)
174 Map *map;
176 map = g_new (Map, 1);
177 map->prepared = FALSE;
178 map->start = start;
179 map->size = end - start;
181 map->offset = offset;
183 map->name = g_strdup (file);
185 process->map_list = g_list_prepend (process->map_list, map);
189 fclose (in);
192 static Map *
193 real_locate_map (MPProcess *process, guint addr)
195 GList *tmp_list = process->map_list;
197 while (tmp_list)
199 Map *tmp_map = tmp_list->data;
201 if ((addr >= tmp_map->start) &&
202 (addr < tmp_map->start + tmp_map->size))
203 return tmp_map;
205 tmp_list = tmp_list->next;
208 return NULL;
211 Map *
212 locate_map (MPProcess *process, guint addr)
214 Map *map = real_locate_map (process, addr);
215 if (!map)
217 gpointer page_addr = (gpointer) (addr - addr % MP_PAGE_SIZE);
218 if (g_list_find (process->bad_pages, page_addr))
219 return NULL;
221 process_read_maps (process);
223 map = real_locate_map (process, addr);
225 if (!map) {
226 process->bad_pages = g_list_prepend (process->bad_pages, page_addr);
227 return NULL;
231 if (!map->prepared)
232 prepare_map (map);
234 return map;
237 const char *
238 process_locate_symbol (MPProcess *process, guint addr)
240 Map *map = locate_map (process, addr);
241 const BinSymbol *symbol;
243 if (!map)
244 return NULL;
246 addr -= map->start;
247 addr += map->offset;
249 symbol = bin_file_lookup_symbol (map->binfile, addr);
251 return bin_symbol_get_name (map->binfile, symbol);
254 gboolean
255 process_find_line (MPProcess *process, void *address,
256 const char **filename, char **functionname,
257 unsigned int *line)
259 char *s = process_locate_symbol (process, address);
261 if (s)
263 *filename = NULL;
264 *functionname = s;
265 *line = -1;
268 return TRUE;
271 void
272 process_dump_stack (MPProcess *process, FILE *out, StackNode *stack)
274 for (; stack != NULL; stack = stack->parent)
276 const char *filename;
277 char *functionname;
278 unsigned int line;
280 if (process_find_line (process, stack->address,
281 &filename, &functionname, &line)) {
282 if (filename)
283 fprintf(out, "\t%s(): %s:%u\n", functionname, filename, line);
284 else
285 fprintf(out, "\t%s()\n", functionname);
286 } else
287 fprintf(out, "\t[%p]\n", stack->address);
292 void
293 process_map_sections (Map *map, SectionFunc func, gpointer user_data)
295 /* FIXME: this should be reinstated */
297 #if 0
298 asection *section;
300 if (map->abfd)
301 for (section = map->abfd->sections; section; section = section->next) {
302 if (strcmp (section->name, ".bss") == 0 ||
303 strcmp (section->name, ".data") == 0) {
304 void *addr = (void *)section->vma;
305 if (map->do_offset)
306 addr += map->addr;
308 /* bfd_section_size() gives 0 for old versions of binutils, so peek
309 * into the internals instead. :-(
311 (*func) (addr, bfd_section_size (map->abfd, section), user_data);
312 // (*func) (addr, section->_cooked_size, user_data);
315 #endif
318 void
319 process_sections (MPProcess *process, SectionFunc func, gpointer user_data)
321 GList *tmp_list;
323 process_read_maps (process);
325 tmp_list = process->map_list;
327 while (tmp_list) {
328 Map *map = tmp_list->data;
330 if (!map->prepared)
331 prepare_map (map);
333 process_map_sections (map, func, user_data);
334 tmp_list = tmp_list->next;
338 /************************************************************
339 * Communication with subprocess
340 ************************************************************/
342 static GHashTable *
343 get_block_table (MPProcess *process)
345 if (!process->block_table)
346 process->block_table = g_hash_table_new (g_direct_hash, NULL);
348 return process->block_table;
352 static void
353 process_free_block (gpointer key, gpointer value, gpointer data)
355 block_unref (value);
358 static void
359 process_duplicate_block (gpointer key, gpointer value, gpointer data)
361 GHashTable *new_table = data;
362 Block *block = value;
364 block->refcount++;
366 g_hash_table_insert (new_table, key, value);
369 MPProcess *
370 process_duplicate (MPProcess *process)
372 MPProcess *new_process = process_new (process->server);
374 if (process->block_table) {
375 GHashTable *new_block_table = get_block_table(new_process);
377 g_hash_table_foreach (process->block_table,
378 process_duplicate_block,
379 new_block_table);
382 new_process->bytes_used = process->bytes_used;
383 new_process->n_allocations = process->n_allocations;
384 new_process->seqno = process->seqno;
386 new_process->parent = process;
388 return new_process;
391 static void
392 process_reinit (MPProcess *process)
394 process_stop_input (process);
396 if (process->input_channel) {
397 g_io_channel_unref (process->input_channel);
398 process->input_channel = NULL;
401 process->bytes_used = 0;
402 process->n_allocations = 0;
403 process->seqno = 0;
405 if (process->map_list) {
406 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
407 g_list_free (process->map_list);
409 process->map_list = NULL;
412 if (process->bad_pages) {
413 g_list_free (process->bad_pages);
414 process->bad_pages = NULL;
417 if (process->block_table) {
418 g_hash_table_foreach (process->block_table, process_free_block, NULL);
419 g_hash_table_destroy (process->block_table);
420 process->block_table = NULL;
423 if (process->stack_stash) {
424 stack_stash_unref (process->stack_stash);
425 process->stack_stash = NULL;
428 /* FIXME: leak */
429 process->command_queue = NULL;
432 static StackStash *
433 get_stack_stash (MPProcess *process)
435 if (!process->stack_stash)
436 process->stack_stash = stack_stash_new (NULL);
438 return process->stack_stash;
441 void
442 process_exec_reset (MPProcess *process)
444 process_reinit (process);
445 g_signal_emit_by_name (process, "reset");
448 static void
449 process_command (MPProcess *process, MIInfo *info, StackNode *stack)
451 GHashTable *block_table;
452 Block *block;
454 if (info->any.seqno != process->seqno) {
455 queue_command (process, info, stack);
456 return;
459 process->seqno++;
461 block = NULL;
463 switch (info->operation) {
464 case MI_NEW:
465 case MI_FORK:
466 case MI_CLONE:
467 case MI_EXEC:
468 g_assert_not_reached ();
469 break;
471 case MI_EXIT:
472 /* Handled before, ignore */
473 break;
475 case MI_TIME:
476 info->alloc.old_ptr = NULL;
477 info->alloc.new_ptr = (void *)process->seqno;
478 info->alloc.size = 1;
479 /* Fall through */
481 default: /* MALLOC / REALLOC / FREE */
482 block_table = get_block_table (process);
484 if (info->alloc.old_ptr != NULL) {
485 block = g_hash_table_lookup (block_table, info->alloc.old_ptr);
486 if (!block) {
487 g_warning ("Block %p not found (pid=%d)!\n", info->alloc.old_ptr, process->pid);
488 process_dump_stack (process, stderr, stack);
490 else {
491 g_hash_table_remove (block_table, info->alloc.old_ptr);
492 process->bytes_used -= block->size;
493 block_unref (block);
495 process->n_allocations--;
499 /* We need to lookup before inserting, because realloc() can call malloc(), so we
500 * see the same block twice. The same problem comes upduring malloc initialization
501 * where __libc_malloc() is called twice for the same block. We could optimize
502 * things a bit by using g_hash_table_new_full() to catch the replacement when
503 * it happens and free the old block, but that would make keeping track of
504 * process->n_allocations/bytes_used a little difficult.
507 if (info->alloc.new_ptr && !g_hash_table_lookup (block_table, info->alloc.new_ptr)) {
508 block = g_new (Block, 1);
509 block->refcount = 1;
511 block->flags = 0;
512 block->addr = info->alloc.new_ptr;
513 block->size = info->alloc.size;
514 block->stack = stack;
516 process->n_allocations++;
517 process->bytes_used += info->alloc.size;
519 g_hash_table_insert (block_table, info->alloc.new_ptr, block);
523 while (process->command_queue && unqueue_command (process, info, &stack))
524 process_command (process, info, stack);
527 static gboolean
528 input_func (GIOChannel *source,
529 GIOCondition condition,
530 gpointer data)
532 MIInfo info;
533 guint count;
534 MPProcess *input_process = data;
535 MPProcess *process = NULL;
537 do {
538 memset (&info, 0, sizeof (info));
539 info.operation = -1;
540 count = 0;
541 if (g_io_channel_get_flags(source) & G_IO_FLAG_IS_READABLE)
542 g_io_channel_read_chars (source, (char *)&info, sizeof(info), &count, NULL);
544 if (count == 0) {
545 g_io_channel_unref (input_process->input_channel);
546 input_process->input_channel = NULL;
548 mp_server_remove_process (input_process->server, input_process);
549 process_set_status (input_process, MP_PROCESS_DEFUNCT);
551 return FALSE;
552 } else {
553 StackNode *stack = NULL;
555 // fprintf (stderr, "Read size %d op 0x%x\n", count, info.operation);
557 if (info.operation == MI_MALLOC ||
558 info.operation == MI_REALLOC ||
559 info.operation == MI_FREE ||
560 info.operation == MI_TIME) {
561 void **stack_buffer = NULL;
562 StackStash *stash = get_stack_stash (input_process);
564 g_assert (count >= sizeof (MIInfoAlloc));
566 stack_buffer = g_alloca (sizeof (void *) * info.alloc.stack_size);
567 g_io_channel_read_chars (source, (char *)stack_buffer, sizeof(void *) * info.alloc.stack_size, &count, NULL);
568 stack = stack_stash_add_trace (stash, stack_buffer, info.alloc.stack_size, -1);
570 } else if (info.operation == MI_EXIT) {
571 process_set_status (input_process, MP_PROCESS_EXITING);
572 if (input_process->clone_of)
573 process_detach (input_process);
576 process = input_process;
577 while (process->clone_of)
578 process = process->clone_of;
580 process_command (process, &info, stack);
583 /* if (info.any.pid != input_process->pid)
584 g_warning ("Ow! Ow! Ow: %d %d %d!", info.any.pid, input_process->pid, g_io_channel_unix_get_fd (input_process->input_channel)); */
587 } while (info.operation != MI_EXIT && (g_io_channel_get_buffer_condition(source) & G_IO_IN));
589 return TRUE;
592 void
593 process_stop_input (MPProcess *process)
595 g_return_if_fail (process != NULL);
597 if (process->input_tag) {
598 g_source_remove (process->input_tag);
599 process->input_tag = 0;
603 void
604 process_start_input (MPProcess *process)
606 g_return_if_fail (process != NULL);
608 if (!process->input_tag && process->input_channel)
609 process->input_tag = g_io_add_watch_full (process->input_channel,
610 G_PRIORITY_LOW,
611 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
612 input_func, process, NULL);
615 void
616 process_clear_input (MPProcess *process)
618 g_return_if_fail (process != NULL);
620 process->bytes_used = 0;
621 process->n_allocations = 0;
623 if (process->map_list) {
624 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
625 g_list_free (process->map_list);
627 process->map_list = NULL;
630 if (process->bad_pages) {
631 g_list_free (process->bad_pages);
632 process->bad_pages = NULL;
635 if (process->block_table) {
636 g_hash_table_foreach (process->block_table, process_free_block, NULL);
637 g_hash_table_destroy (process->block_table);
638 process->block_table = NULL;
641 if (process->stack_stash) {
642 stack_stash_unref (process->stack_stash);
643 process->stack_stash = NULL;
647 char *
648 process_find_exec (char **args)
650 int i;
652 if (g_file_test(args[0], G_FILE_TEST_EXISTS)) {
653 if (!g_path_is_absolute (args[0]))
654 return g_strconcat ("./", args[0], NULL);
655 else
656 return g_strdup (args[0]);
657 } else {
658 char **paths;
659 char *path = NULL;
660 char *pathenv = getenv ("PATH");
661 if (pathenv)
663 paths = g_strsplit (pathenv, ":", -1);
664 for (i=0; paths[i]; i++) {
665 path = g_build_filename (paths[i], args[0], NULL);
666 if (g_file_test (path, G_FILE_TEST_EXISTS))
667 break;
668 else {
669 g_free (path);
670 path = NULL;
674 g_strfreev (paths);
677 return path;
681 char **
682 process_parse_exec (const char *exec_string)
684 return g_strsplit (exec_string, " ", -1);
687 GType
688 mp_process_get_type (void)
690 static GType process_type = 0;
692 if (!process_type) {
693 static const GTypeInfo process_info = {
694 sizeof (MPProcessClass),
695 (GBaseInitFunc) NULL,
696 (GBaseFinalizeFunc) NULL,
697 (GClassInitFunc) mp_process_class_init,
698 NULL, /* class_finalize */
699 NULL, /* class_data */
700 sizeof (MPProcess),
701 0, /* n_preallocs */
702 (GInstanceInitFunc) mp_process_init
705 process_type = g_type_register_static (G_TYPE_OBJECT,
706 "MPProcess",
707 &process_info, 0);
710 return process_type;
713 static void
714 mp_process_class_init (MPProcessClass *class)
716 static gboolean initialized = FALSE;
718 GObjectClass *o_class = G_OBJECT_CLASS (class);
720 o_class->finalize = mp_process_finalize;
722 if (!initialized) {
723 process_signals[STATUS_CHANGED] =
724 g_signal_new ("status_changed",
725 MP_TYPE_PROCESS,
726 G_SIGNAL_RUN_LAST,
727 G_STRUCT_OFFSET (MPProcessClass, status_changed),
728 NULL, NULL,
729 g_cclosure_marshal_VOID__VOID,
730 G_TYPE_NONE, 0);
732 process_signals[RESET] =
733 g_signal_new ("reset",
734 MP_TYPE_PROCESS,
735 G_SIGNAL_RUN_LAST,
736 G_STRUCT_OFFSET (MPProcessClass, reset),
737 NULL, NULL,
738 g_cclosure_marshal_VOID__VOID,
739 G_TYPE_NONE, 0);
741 initialized = TRUE;
745 static void
746 mp_process_init (MPProcess *process)
748 process->status = MP_PROCESS_INIT;
749 process->pid = 0;
750 process->clone_of = NULL;
752 process->bytes_used = 0;
753 process->n_allocations = 0;
754 process->block_table = NULL;
755 process->stack_stash = NULL;
757 process->program_name = NULL;
758 process->input_channel = NULL;
760 process->seqno = 0;
762 process->command_queue = NULL;
764 process->follow_fork = FALSE;
765 process->follow_exec = FALSE;
767 g_object_ref (G_OBJECT (process));
770 static void
771 mp_process_finalize (GObject *object)
773 MPProcess *process = MP_PROCESS (object);
775 process_reinit (process);
777 g_free (process->program_name);
781 MPProcess *
782 process_new (MPServer *server)
784 MPProcess *process;
786 process = g_object_new (MP_TYPE_PROCESS, NULL);
788 process->server = server;
790 return process;
793 void
794 process_set_follow_fork (MPProcess *process,
795 gboolean follow_fork)
797 process->follow_fork = follow_fork;
800 void
801 process_set_follow_exec (MPProcess *process,
802 gboolean follow_exec)
804 process->follow_exec = follow_exec;
807 void
808 process_run (MPProcess *process, const char *path, char **args)
810 process->program_name = g_strdup (path);
811 read_inode (path);
813 process->pid = mp_server_instrument (process->server, path, args);
814 mp_server_add_process (process->server, process);
816 process_set_status (process, MP_PROCESS_STARTING);
818 process_start_input (process);
821 GList *
822 process_get_clones (MPProcess *process)
824 return mp_server_get_process_clones (process->server, process);
827 void
828 process_set_status (MPProcess *process, MPProcessStatus status)
830 if (process->status != status) {
831 process->status = status;
832 g_signal_emit_by_name (process, "status_changed", NULL);
836 char *
837 process_get_status_text (MPProcess *process)
839 char *status = "";
841 switch (process->status) {
842 case MP_PROCESS_INIT:
843 status = _("Initial");
844 break;
845 case MP_PROCESS_STARTING:
846 status = _("Starting");
847 break;
848 case MP_PROCESS_RUNNING:
849 status = _("Running");
850 break;
851 case MP_PROCESS_EXITING:
852 status = _("Exiting");
853 break;
854 case MP_PROCESS_DEFUNCT:
855 status = _("Defunct");
856 break;
857 case MP_PROCESS_DETACHED:
858 status = _("Defunct");
859 break;
862 return g_strdup (status);
865 char *
866 process_get_cmdline (MPProcess *process)
868 char *fname;
869 char *result;
870 char *tmp = NULL;
871 int n = 0;
872 FILE *in = NULL;
874 if (process->status == MP_PROCESS_DEFUNCT)
875 return g_strdup ("");
877 fname = g_strdup_printf ("/proc/%d/cmdline", process->pid);
878 in = fopen (fname, "r");
879 if (!in) {
880 g_warning ("Can't open %s\n", fname);
881 return g_strdup ("");
883 g_free (fname);
885 getline (&tmp, &n, in);
886 result = g_strdup (tmp);
887 free (tmp);
889 fclose (in);
891 return result;
894 void
895 process_detach (MPProcess *process)
897 if (process->status != MP_PROCESS_DEFUNCT) {
898 int fd = g_io_channel_unix_get_fd (process->input_channel);
900 if (process->status == MP_PROCESS_EXITING) {
901 char response = 0;
902 write (fd, &response, 1);
903 } else {
904 g_io_channel_shutdown (process->input_channel, TRUE, NULL);
905 process_set_status (process, MP_PROCESS_DETACHED);
910 void
911 process_kill (MPProcess *process)
913 if (process->status == MP_PROCESS_EXITING) {
914 process_detach (process);
915 } else if (process->status != MP_PROCESS_DEFUNCT &&
916 process->status != MP_PROCESS_INIT) {
917 kill (process->pid, SIGTERM);
921 typedef struct {
922 MPProcessBlockForeachFunc foreach_func;
923 gpointer data;
924 } BlockForeachInfo;
926 static void
927 block_table_foreach_func (gpointer key,
928 gpointer value,
929 gpointer data)
931 BlockForeachInfo *info = data;
933 info->foreach_func (value, info->data);
936 void
937 process_block_foreach (MPProcess *process,
938 MPProcessBlockForeachFunc foreach_func,
939 gpointer data)
941 GHashTable *block_table = get_block_table (process);
942 BlockForeachInfo info;
944 info.foreach_func = foreach_func;
945 info.data = data;
947 g_hash_table_foreach (block_table, block_table_foreach_func, &info);
950 static gboolean
951 my_str_equal (const char *s1, const char *s2)
953 return s1 == s2;
956 gboolean
957 symbol_equal (gconstpointer s1, gconstpointer s2)
959 return s1 == s2;
962 guint
963 symbol_hash (gconstpointer s)
965 const char *symbol = s;
967 return g_str_hash (s);
970 char *
971 symbol_copy (const char *orig)
973 if (!orig)
974 return NULL;
976 return orig;
979 void
980 symbol_free (char *symbol)
982 if (!symbol)
983 return;