src/main.c: Make it possible to pass options to the app under test
[memprof.git] / src / process.c
blob84612935c0a8060d151da6dfeea28b6d76e23913
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 void
127 prepare_map (Map *map)
129 g_return_if_fail (!map->prepared);
131 map->binfile = bin_file_new (map->name);
133 map->prepared = TRUE;
136 static void
137 process_read_maps (MPProcess *process)
139 gchar buffer[1024];
140 FILE *in;
142 snprintf (buffer, 1023, "/proc/%d/maps", process->pid);
144 in = fopen (buffer, "r");
146 if (process->map_list) {
147 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
148 g_list_free (process->map_list);
150 process->map_list = NULL;
153 while (fgets(buffer, 1023, in)) {
154 char file[256];
155 int count;
156 gulong start;
157 gulong end;
158 gulong offset;
159 gulong inode;
161 count = sscanf (
162 buffer, "%lx-%lx %*15s %lx %*x:%*x %lu %255s",
163 &start, &end, &offset, &inode, file);
164 if (count == 5)
166 Map *map;
168 map = g_new (Map, 1);
169 map->prepared = FALSE;
170 map->start = start;
171 map->size = end - start;
173 map->offset = offset;
175 map->name = g_strdup (file);
177 process->map_list = g_list_prepend (process->map_list, map);
181 fclose (in);
184 static Map *
185 real_locate_map (MPProcess *process, guint addr)
187 GList *tmp_list = process->map_list;
189 while (tmp_list)
191 Map *tmp_map = tmp_list->data;
193 if ((addr >= tmp_map->start) &&
194 (addr < tmp_map->start + tmp_map->size))
195 return tmp_map;
197 tmp_list = tmp_list->next;
200 return NULL;
203 Map *
204 locate_map (MPProcess *process, guint addr)
206 Map *map = real_locate_map (process, addr);
207 if (!map)
209 gpointer page_addr = (gpointer) (addr - addr % MP_PAGE_SIZE);
210 if (g_list_find (process->bad_pages, page_addr))
211 return NULL;
213 process_read_maps (process);
215 map = real_locate_map (process, addr);
217 if (!map) {
218 process->bad_pages = g_list_prepend (process->bad_pages, page_addr);
219 return NULL;
223 if (!map->prepared)
224 prepare_map (map);
226 return map;
229 const char *
230 process_locate_symbol (MPProcess *process, gsize addr)
232 Map *map = locate_map (process, addr);
233 const BinSymbol *symbol;
235 if (!map)
236 return "<unknown map>";
238 addr -= map->start;
239 addr += map->offset;
241 symbol = bin_file_lookup_symbol (map->binfile, addr);
243 return bin_symbol_get_name (map->binfile, symbol);
247 * The string assigned to functionname is owned/shared
248 * by the system and must not be freed.
250 gboolean
251 process_find_line (MPProcess *process, void *address,
252 const char **filename, char **functionname,
253 unsigned int *line)
255 const char *s = process_locate_symbol (process, GPOINTER_TO_SIZE (address));
257 if (s)
259 *filename = NULL;
260 *functionname = (char*)s;
261 *line = -1;
264 return TRUE;
267 void
268 process_dump_stack (MPProcess *process, FILE *out, StackNode *stack)
270 for (; stack != NULL; stack = stack->parent)
272 const char *filename;
273 char *functionname;
274 unsigned int line;
276 if (process_find_line (process, stack->address,
277 &filename, &functionname, &line)) {
278 if (filename)
279 fprintf(out, "\t%s(): %s:%u\n", functionname, filename, line);
280 else
281 fprintf(out, "\t%s()\n", functionname);
282 } else
283 fprintf(out, "\t[%p]\n", stack->address);
288 void
289 process_map_sections (Map *map, SectionFunc func, gpointer user_data)
291 /* FIXME: this should be reinstated */
293 #if 0
294 asection *section;
296 if (map->abfd)
297 for (section = map->abfd->sections; section; section = section->next) {
298 if (strcmp (section->name, ".bss") == 0 ||
299 strcmp (section->name, ".data") == 0) {
300 void *addr = (void *)section->vma;
301 if (map->do_offset)
302 addr += map->addr;
304 /* bfd_section_size() gives 0 for old versions of binutils, so peek
305 * into the internals instead. :-(
307 (*func) (addr, bfd_section_size (map->abfd, section), user_data);
308 // (*func) (addr, section->_cooked_size, user_data);
311 #endif
314 void
315 process_sections (MPProcess *process, SectionFunc func, gpointer user_data)
317 GList *tmp_list;
319 process_read_maps (process);
321 tmp_list = process->map_list;
323 while (tmp_list) {
324 Map *map = tmp_list->data;
326 if (!map->prepared)
327 prepare_map (map);
329 process_map_sections (map, func, user_data);
330 tmp_list = tmp_list->next;
334 /************************************************************
335 * Communication with subprocess
336 ************************************************************/
338 static GHashTable *
339 get_block_table (MPProcess *process)
341 if (!process->block_table)
342 process->block_table = g_hash_table_new (g_direct_hash, NULL);
344 return process->block_table;
348 static void
349 process_free_block (gpointer key, gpointer value, gpointer data)
351 block_unref (value);
354 static void
355 process_duplicate_block (gpointer key, gpointer value, gpointer data)
357 GHashTable *new_table = data;
358 Block *block = value;
360 block->refcount++;
362 g_hash_table_insert (new_table, key, value);
365 MPProcess *
366 process_duplicate (MPProcess *process)
368 MPProcess *new_process = process_new (process->server);
370 if (process->block_table) {
371 GHashTable *new_block_table = get_block_table(new_process);
373 g_hash_table_foreach (process->block_table,
374 process_duplicate_block,
375 new_block_table);
378 new_process->bytes_used = process->bytes_used;
379 new_process->n_allocations = process->n_allocations;
380 new_process->seqno = process->seqno;
382 new_process->parent = process;
384 return new_process;
387 static void
388 process_reinit (MPProcess *process)
390 process_stop_input (process);
392 if (process->input_channel) {
393 g_io_channel_unref (process->input_channel);
394 process->input_channel = NULL;
397 process->bytes_used = 0;
398 process->n_allocations = 0;
399 process->seqno = 0;
401 if (process->map_list) {
402 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
403 g_list_free (process->map_list);
405 process->map_list = NULL;
408 if (process->bad_pages) {
409 g_list_free (process->bad_pages);
410 process->bad_pages = NULL;
413 if (process->block_table) {
414 g_hash_table_foreach (process->block_table, process_free_block, NULL);
415 g_hash_table_destroy (process->block_table);
416 process->block_table = NULL;
419 if (process->stack_stash) {
420 stack_stash_unref (process->stack_stash);
421 process->stack_stash = NULL;
424 /* FIXME: leak */
425 process->command_queue = NULL;
428 static StackStash *
429 get_stack_stash (MPProcess *process)
431 if (!process->stack_stash)
432 process->stack_stash = stack_stash_new (NULL);
434 return process->stack_stash;
437 void
438 process_exec_reset (MPProcess *process)
440 process_reinit (process);
441 g_signal_emit_by_name (process, "reset");
444 static void
445 process_command (MPProcess *process, MIInfo *info, StackNode *stack)
447 GHashTable *block_table;
448 Block *block;
450 if (info->any.seqno != process->seqno) {
451 queue_command (process, info, stack);
452 return;
455 process->seqno++;
457 block = NULL;
459 switch (info->operation) {
460 case MI_NEW:
461 case MI_FORK:
462 case MI_CLONE:
463 case MI_EXEC:
464 g_assert_not_reached ();
465 break;
467 case MI_EXIT:
468 /* Handled before, ignore */
469 break;
471 case MI_TIME:
472 info->alloc.old_ptr = NULL;
473 info->alloc.new_ptr = (void *)process->seqno;
474 info->alloc.size = 1;
475 /* Fall through */
477 default: /* MALLOC / REALLOC / FREE */
478 block_table = get_block_table (process);
480 if (info->alloc.old_ptr != NULL) {
481 block = g_hash_table_lookup (block_table, info->alloc.old_ptr);
482 if (!block) {
483 g_warning ("Block %p not found (pid=%d)!\n", info->alloc.old_ptr, process->pid);
484 process_dump_stack (process, stderr, stack);
486 else {
487 g_hash_table_remove (block_table, info->alloc.old_ptr);
488 process->bytes_used -= block->size;
489 block_unref (block);
491 process->n_allocations--;
495 /* We need to lookup before inserting, because realloc() can call malloc(), so we
496 * see the same block twice. The same problem comes upduring malloc initialization
497 * where __libc_malloc() is called twice for the same block. We could optimize
498 * things a bit by using g_hash_table_new_full() to catch the replacement when
499 * it happens and free the old block, but that would make keeping track of
500 * process->n_allocations/bytes_used a little difficult.
503 if (info->alloc.new_ptr && !g_hash_table_lookup (block_table, info->alloc.new_ptr)) {
504 block = g_new (Block, 1);
505 block->refcount = 1;
507 block->flags = 0;
508 block->addr = info->alloc.new_ptr;
509 block->size = info->alloc.size;
510 block->stack = stack;
512 process->n_allocations++;
513 process->bytes_used += info->alloc.size;
515 g_hash_table_insert (block_table, info->alloc.new_ptr, block);
519 while (process->command_queue && unqueue_command (process, info, &stack))
520 process_command (process, info, stack);
523 static gboolean
524 input_func (GIOChannel *source,
525 GIOCondition condition,
526 gpointer data)
528 MIInfo info;
529 guint count;
530 MPProcess *input_process = data;
531 MPProcess *process = NULL;
533 do {
534 memset (&info, 0, sizeof (info));
535 info.operation = -1;
536 count = 0;
537 if (g_io_channel_get_flags(source) & G_IO_FLAG_IS_READABLE)
538 g_io_channel_read_chars (source, (char *)&info, sizeof(info), &count, NULL);
540 if (count == 0) {
541 g_io_channel_unref (input_process->input_channel);
542 input_process->input_channel = NULL;
544 mp_server_remove_process (input_process->server, input_process);
545 process_set_status (input_process, MP_PROCESS_DEFUNCT);
547 return FALSE;
548 } else {
549 StackNode *stack = NULL;
551 // fprintf (stderr, "Read size %d op 0x%x\n", count, info.operation);
553 if (info.operation == MI_MALLOC ||
554 info.operation == MI_REALLOC ||
555 info.operation == MI_FREE ||
556 info.operation == MI_TIME) {
557 void **stack_buffer = NULL;
558 StackStash *stash = get_stack_stash (input_process);
560 g_assert (count >= sizeof (MIInfoAlloc));
562 stack_buffer = g_alloca (sizeof (void *) * info.alloc.stack_size);
563 g_io_channel_read_chars (source, (char *)stack_buffer, sizeof(void *) * info.alloc.stack_size, &count, NULL);
564 stack = stack_stash_add_trace (stash, stack_buffer, info.alloc.stack_size, -1);
566 } else if (info.operation == MI_EXIT) {
567 process_set_status (input_process, MP_PROCESS_EXITING);
568 if (input_process->clone_of)
569 process_detach (input_process);
572 process = input_process;
573 while (process->clone_of)
574 process = process->clone_of;
576 process_command (process, &info, stack);
579 /* if (info.any.pid != input_process->pid)
580 g_warning ("Ow! Ow! Ow: %d %d %d!", info.any.pid, input_process->pid, g_io_channel_unix_get_fd (input_process->input_channel)); */
583 } while (info.operation != MI_EXIT && (g_io_channel_get_buffer_condition(source) & G_IO_IN));
585 return TRUE;
588 void
589 process_stop_input (MPProcess *process)
591 g_return_if_fail (process != NULL);
593 if (process->input_tag) {
594 g_source_remove (process->input_tag);
595 process->input_tag = 0;
599 void
600 process_start_input (MPProcess *process)
602 g_return_if_fail (process != NULL);
604 if (!process->input_tag && process->input_channel)
605 process->input_tag = g_io_add_watch_full (process->input_channel,
606 G_PRIORITY_LOW,
607 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
608 input_func, process, NULL);
611 void
612 process_clear_input (MPProcess *process)
614 g_return_if_fail (process != NULL);
616 process->bytes_used = 0;
617 process->n_allocations = 0;
619 if (process->map_list) {
620 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
621 g_list_free (process->map_list);
623 process->map_list = NULL;
626 if (process->bad_pages) {
627 g_list_free (process->bad_pages);
628 process->bad_pages = NULL;
631 if (process->block_table) {
632 g_hash_table_foreach (process->block_table, process_free_block, NULL);
633 g_hash_table_destroy (process->block_table);
634 process->block_table = NULL;
637 if (process->stack_stash) {
638 stack_stash_unref (process->stack_stash);
639 process->stack_stash = NULL;
643 char *
644 process_find_exec (char **args)
646 int i;
648 if (g_file_test(args[0], G_FILE_TEST_EXISTS)) {
649 if (!g_path_is_absolute (args[0]))
650 return g_strconcat ("./", args[0], NULL);
651 else
652 return g_strdup (args[0]);
653 } else {
654 char **paths;
655 char *path = NULL;
656 char *pathenv = getenv ("PATH");
657 if (pathenv)
659 paths = g_strsplit (pathenv, ":", -1);
660 for (i=0; paths[i]; i++) {
661 path = g_build_filename (paths[i], args[0], NULL);
662 if (g_file_test (path, G_FILE_TEST_EXISTS))
663 break;
664 else {
665 g_free (path);
666 path = NULL;
670 g_strfreev (paths);
673 return path;
677 char **
678 process_parse_exec (const char *exec_string)
680 return g_strsplit (exec_string, " ", -1);
683 GType
684 mp_process_get_type (void)
686 static GType process_type = 0;
688 if (!process_type) {
689 static const GTypeInfo process_info = {
690 sizeof (MPProcessClass),
691 (GBaseInitFunc) NULL,
692 (GBaseFinalizeFunc) NULL,
693 (GClassInitFunc) mp_process_class_init,
694 NULL, /* class_finalize */
695 NULL, /* class_data */
696 sizeof (MPProcess),
697 0, /* n_preallocs */
698 (GInstanceInitFunc) mp_process_init
701 process_type = g_type_register_static (G_TYPE_OBJECT,
702 "MPProcess",
703 &process_info, 0);
706 return process_type;
709 static void
710 mp_process_class_init (MPProcessClass *class)
712 static gboolean initialized = FALSE;
714 GObjectClass *o_class = G_OBJECT_CLASS (class);
716 o_class->finalize = mp_process_finalize;
718 if (!initialized) {
719 process_signals[STATUS_CHANGED] =
720 g_signal_new ("status_changed",
721 MP_TYPE_PROCESS,
722 G_SIGNAL_RUN_LAST,
723 G_STRUCT_OFFSET (MPProcessClass, status_changed),
724 NULL, NULL,
725 g_cclosure_marshal_VOID__VOID,
726 G_TYPE_NONE, 0);
728 process_signals[RESET] =
729 g_signal_new ("reset",
730 MP_TYPE_PROCESS,
731 G_SIGNAL_RUN_LAST,
732 G_STRUCT_OFFSET (MPProcessClass, reset),
733 NULL, NULL,
734 g_cclosure_marshal_VOID__VOID,
735 G_TYPE_NONE, 0);
737 initialized = TRUE;
741 static void
742 mp_process_init (MPProcess *process)
744 process->status = MP_PROCESS_INIT;
745 process->pid = 0;
746 process->clone_of = NULL;
748 process->bytes_used = 0;
749 process->n_allocations = 0;
750 process->block_table = NULL;
751 process->stack_stash = NULL;
753 process->program_name = NULL;
754 process->input_channel = NULL;
756 process->seqno = 0;
758 process->command_queue = NULL;
760 process->follow_fork = FALSE;
761 process->follow_exec = FALSE;
763 g_object_ref (G_OBJECT (process));
766 static void
767 mp_process_finalize (GObject *object)
769 MPProcess *process = MP_PROCESS (object);
771 process_reinit (process);
773 g_free (process->program_name);
777 MPProcess *
778 process_new (MPServer *server)
780 MPProcess *process;
782 process = g_object_new (MP_TYPE_PROCESS, NULL);
784 process->server = server;
786 return process;
789 void
790 process_set_follow_fork (MPProcess *process,
791 gboolean follow_fork)
793 process->follow_fork = follow_fork;
796 void
797 process_set_follow_exec (MPProcess *process,
798 gboolean follow_exec)
800 process->follow_exec = follow_exec;
803 void
804 process_run (MPProcess *process, const char *path, char **args)
806 process->program_name = g_strdup (path);
807 read_inode (path);
809 process->pid = mp_server_instrument (process->server, path, args);
810 mp_server_add_process (process->server, process);
812 process_set_status (process, MP_PROCESS_STARTING);
814 process_start_input (process);
817 GList *
818 process_get_clones (MPProcess *process)
820 return mp_server_get_process_clones (process->server, process);
823 void
824 process_set_status (MPProcess *process, MPProcessStatus status)
826 if (process->status != status) {
827 process->status = status;
828 g_signal_emit_by_name (process, "status_changed", NULL);
832 char *
833 process_get_status_text (MPProcess *process)
835 char *status = "";
837 switch (process->status) {
838 case MP_PROCESS_INIT:
839 status = _("Initial");
840 break;
841 case MP_PROCESS_STARTING:
842 status = _("Starting");
843 break;
844 case MP_PROCESS_RUNNING:
845 status = _("Running");
846 break;
847 case MP_PROCESS_EXITING:
848 status = _("Exiting");
849 break;
850 case MP_PROCESS_DEFUNCT:
851 status = _("Defunct");
852 break;
853 case MP_PROCESS_DETACHED:
854 status = _("Defunct");
855 break;
858 return g_strdup (status);
861 char *
862 process_get_cmdline (MPProcess *process)
864 char *fname;
865 char *result;
866 char *tmp = NULL;
867 size_t n = 0;
868 FILE *in = NULL;
870 if (process->status == MP_PROCESS_DEFUNCT)
871 return g_strdup ("");
873 fname = g_strdup_printf ("/proc/%d/cmdline", process->pid);
874 in = fopen (fname, "r");
875 if (!in) {
876 g_warning ("Can't open %s\n", fname);
877 return g_strdup ("");
879 g_free (fname);
881 if (getline (&tmp, &n, in) == -1)
882 result = g_strdup ("");
883 else {
884 result = g_strdup (tmp);
885 free (tmp);
888 fclose (in);
890 return result;
893 void
894 process_detach (MPProcess *process)
896 int ret;
898 if (process->status != MP_PROCESS_DEFUNCT) {
899 int fd = g_io_channel_unix_get_fd (process->input_channel);
901 if (process->status == MP_PROCESS_EXITING) {
902 char response = 0;
903 ret = write (fd, &response, 1);
904 } else {
905 g_io_channel_shutdown (process->input_channel, TRUE, NULL);
906 process_set_status (process, MP_PROCESS_DETACHED);
911 void
912 process_kill (MPProcess *process)
914 if (process->status == MP_PROCESS_EXITING) {
915 process_detach (process);
916 } else if (process->status != MP_PROCESS_DEFUNCT &&
917 process->status != MP_PROCESS_INIT) {
918 kill (process->pid, SIGTERM);
922 typedef struct {
923 MPProcessBlockForeachFunc foreach_func;
924 gpointer data;
925 } BlockForeachInfo;
927 static void
928 block_table_foreach_func (gpointer key,
929 gpointer value,
930 gpointer data)
932 BlockForeachInfo *info = data;
934 info->foreach_func (value, info->data);
937 void
938 process_block_foreach (MPProcess *process,
939 MPProcessBlockForeachFunc foreach_func,
940 gpointer data)
942 GHashTable *block_table = get_block_table (process);
943 BlockForeachInfo info;
945 info.foreach_func = foreach_func;
946 info.data = data;
948 g_hash_table_foreach (block_table, block_table_foreach_func, &info);
951 gboolean
952 symbol_equal (gconstpointer s1, gconstpointer s2)
954 return s1 == s2;
957 guint
958 symbol_hash (gconstpointer s)
960 const char *symbol = s;
962 return g_str_hash (symbol);
965 char *
966 symbol_copy (const char *orig)
968 if (!orig)
969 return NULL;
971 return (char *) orig;
974 void
975 symbol_free (char *symbol)
977 if (!symbol)
978 return;