process.c: Properly cast the const char* to char*
[memprof.git] / src / process.c
blob4fefd1e0b257330e896a6e462cb5057394c5415b
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, guint addr)
232 Map *map = locate_map (process, addr);
233 const BinSymbol *symbol;
235 if (!map)
236 return NULL;
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);
246 gboolean
247 process_find_line (MPProcess *process, void *address,
248 const char **filename, char **functionname,
249 unsigned int *line)
251 const char *s = process_locate_symbol (process, address);
253 if (s)
255 *filename = NULL;
256 *functionname = (char*)s;
257 *line = -1;
260 return TRUE;
263 void
264 process_dump_stack (MPProcess *process, FILE *out, StackNode *stack)
266 for (; stack != NULL; stack = stack->parent)
268 const char *filename;
269 char *functionname;
270 unsigned int line;
272 if (process_find_line (process, stack->address,
273 &filename, &functionname, &line)) {
274 if (filename)
275 fprintf(out, "\t%s(): %s:%u\n", functionname, filename, line);
276 else
277 fprintf(out, "\t%s()\n", functionname);
278 } else
279 fprintf(out, "\t[%p]\n", stack->address);
284 void
285 process_map_sections (Map *map, SectionFunc func, gpointer user_data)
287 /* FIXME: this should be reinstated */
289 #if 0
290 asection *section;
292 if (map->abfd)
293 for (section = map->abfd->sections; section; section = section->next) {
294 if (strcmp (section->name, ".bss") == 0 ||
295 strcmp (section->name, ".data") == 0) {
296 void *addr = (void *)section->vma;
297 if (map->do_offset)
298 addr += map->addr;
300 /* bfd_section_size() gives 0 for old versions of binutils, so peek
301 * into the internals instead. :-(
303 (*func) (addr, bfd_section_size (map->abfd, section), user_data);
304 // (*func) (addr, section->_cooked_size, user_data);
307 #endif
310 void
311 process_sections (MPProcess *process, SectionFunc func, gpointer user_data)
313 GList *tmp_list;
315 process_read_maps (process);
317 tmp_list = process->map_list;
319 while (tmp_list) {
320 Map *map = tmp_list->data;
322 if (!map->prepared)
323 prepare_map (map);
325 process_map_sections (map, func, user_data);
326 tmp_list = tmp_list->next;
330 /************************************************************
331 * Communication with subprocess
332 ************************************************************/
334 static GHashTable *
335 get_block_table (MPProcess *process)
337 if (!process->block_table)
338 process->block_table = g_hash_table_new (g_direct_hash, NULL);
340 return process->block_table;
344 static void
345 process_free_block (gpointer key, gpointer value, gpointer data)
347 block_unref (value);
350 static void
351 process_duplicate_block (gpointer key, gpointer value, gpointer data)
353 GHashTable *new_table = data;
354 Block *block = value;
356 block->refcount++;
358 g_hash_table_insert (new_table, key, value);
361 MPProcess *
362 process_duplicate (MPProcess *process)
364 MPProcess *new_process = process_new (process->server);
366 if (process->block_table) {
367 GHashTable *new_block_table = get_block_table(new_process);
369 g_hash_table_foreach (process->block_table,
370 process_duplicate_block,
371 new_block_table);
374 new_process->bytes_used = process->bytes_used;
375 new_process->n_allocations = process->n_allocations;
376 new_process->seqno = process->seqno;
378 new_process->parent = process;
380 return new_process;
383 static void
384 process_reinit (MPProcess *process)
386 process_stop_input (process);
388 if (process->input_channel) {
389 g_io_channel_unref (process->input_channel);
390 process->input_channel = NULL;
393 process->bytes_used = 0;
394 process->n_allocations = 0;
395 process->seqno = 0;
397 if (process->map_list) {
398 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
399 g_list_free (process->map_list);
401 process->map_list = NULL;
404 if (process->bad_pages) {
405 g_list_free (process->bad_pages);
406 process->bad_pages = NULL;
409 if (process->block_table) {
410 g_hash_table_foreach (process->block_table, process_free_block, NULL);
411 g_hash_table_destroy (process->block_table);
412 process->block_table = NULL;
415 if (process->stack_stash) {
416 stack_stash_unref (process->stack_stash);
417 process->stack_stash = NULL;
420 /* FIXME: leak */
421 process->command_queue = NULL;
424 static StackStash *
425 get_stack_stash (MPProcess *process)
427 if (!process->stack_stash)
428 process->stack_stash = stack_stash_new (NULL);
430 return process->stack_stash;
433 void
434 process_exec_reset (MPProcess *process)
436 process_reinit (process);
437 g_signal_emit_by_name (process, "reset");
440 static void
441 process_command (MPProcess *process, MIInfo *info, StackNode *stack)
443 GHashTable *block_table;
444 Block *block;
446 if (info->any.seqno != process->seqno) {
447 queue_command (process, info, stack);
448 return;
451 process->seqno++;
453 block = NULL;
455 switch (info->operation) {
456 case MI_NEW:
457 case MI_FORK:
458 case MI_CLONE:
459 case MI_EXEC:
460 g_assert_not_reached ();
461 break;
463 case MI_EXIT:
464 /* Handled before, ignore */
465 break;
467 case MI_TIME:
468 info->alloc.old_ptr = NULL;
469 info->alloc.new_ptr = (void *)process->seqno;
470 info->alloc.size = 1;
471 /* Fall through */
473 default: /* MALLOC / REALLOC / FREE */
474 block_table = get_block_table (process);
476 if (info->alloc.old_ptr != NULL) {
477 block = g_hash_table_lookup (block_table, info->alloc.old_ptr);
478 if (!block) {
479 g_warning ("Block %p not found (pid=%d)!\n", info->alloc.old_ptr, process->pid);
480 process_dump_stack (process, stderr, stack);
482 else {
483 g_hash_table_remove (block_table, info->alloc.old_ptr);
484 process->bytes_used -= block->size;
485 block_unref (block);
487 process->n_allocations--;
491 /* We need to lookup before inserting, because realloc() can call malloc(), so we
492 * see the same block twice. The same problem comes upduring malloc initialization
493 * where __libc_malloc() is called twice for the same block. We could optimize
494 * things a bit by using g_hash_table_new_full() to catch the replacement when
495 * it happens and free the old block, but that would make keeping track of
496 * process->n_allocations/bytes_used a little difficult.
499 if (info->alloc.new_ptr && !g_hash_table_lookup (block_table, info->alloc.new_ptr)) {
500 block = g_new (Block, 1);
501 block->refcount = 1;
503 block->flags = 0;
504 block->addr = info->alloc.new_ptr;
505 block->size = info->alloc.size;
506 block->stack = stack;
508 process->n_allocations++;
509 process->bytes_used += info->alloc.size;
511 g_hash_table_insert (block_table, info->alloc.new_ptr, block);
515 while (process->command_queue && unqueue_command (process, info, &stack))
516 process_command (process, info, stack);
519 static gboolean
520 input_func (GIOChannel *source,
521 GIOCondition condition,
522 gpointer data)
524 MIInfo info;
525 guint count;
526 MPProcess *input_process = data;
527 MPProcess *process = NULL;
529 do {
530 memset (&info, 0, sizeof (info));
531 info.operation = -1;
532 count = 0;
533 if (g_io_channel_get_flags(source) & G_IO_FLAG_IS_READABLE)
534 g_io_channel_read_chars (source, (char *)&info, sizeof(info), &count, NULL);
536 if (count == 0) {
537 g_io_channel_unref (input_process->input_channel);
538 input_process->input_channel = NULL;
540 mp_server_remove_process (input_process->server, input_process);
541 process_set_status (input_process, MP_PROCESS_DEFUNCT);
543 return FALSE;
544 } else {
545 StackNode *stack = NULL;
547 // fprintf (stderr, "Read size %d op 0x%x\n", count, info.operation);
549 if (info.operation == MI_MALLOC ||
550 info.operation == MI_REALLOC ||
551 info.operation == MI_FREE ||
552 info.operation == MI_TIME) {
553 void **stack_buffer = NULL;
554 StackStash *stash = get_stack_stash (input_process);
556 g_assert (count >= sizeof (MIInfoAlloc));
558 stack_buffer = g_alloca (sizeof (void *) * info.alloc.stack_size);
559 g_io_channel_read_chars (source, (char *)stack_buffer, sizeof(void *) * info.alloc.stack_size, &count, NULL);
560 stack = stack_stash_add_trace (stash, stack_buffer, info.alloc.stack_size, -1);
562 } else if (info.operation == MI_EXIT) {
563 process_set_status (input_process, MP_PROCESS_EXITING);
564 if (input_process->clone_of)
565 process_detach (input_process);
568 process = input_process;
569 while (process->clone_of)
570 process = process->clone_of;
572 process_command (process, &info, stack);
575 /* if (info.any.pid != input_process->pid)
576 g_warning ("Ow! Ow! Ow: %d %d %d!", info.any.pid, input_process->pid, g_io_channel_unix_get_fd (input_process->input_channel)); */
579 } while (info.operation != MI_EXIT && (g_io_channel_get_buffer_condition(source) & G_IO_IN));
581 return TRUE;
584 void
585 process_stop_input (MPProcess *process)
587 g_return_if_fail (process != NULL);
589 if (process->input_tag) {
590 g_source_remove (process->input_tag);
591 process->input_tag = 0;
595 void
596 process_start_input (MPProcess *process)
598 g_return_if_fail (process != NULL);
600 if (!process->input_tag && process->input_channel)
601 process->input_tag = g_io_add_watch_full (process->input_channel,
602 G_PRIORITY_LOW,
603 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
604 input_func, process, NULL);
607 void
608 process_clear_input (MPProcess *process)
610 g_return_if_fail (process != NULL);
612 process->bytes_used = 0;
613 process->n_allocations = 0;
615 if (process->map_list) {
616 g_list_foreach (process->map_list, (GFunc)g_free, NULL);
617 g_list_free (process->map_list);
619 process->map_list = NULL;
622 if (process->bad_pages) {
623 g_list_free (process->bad_pages);
624 process->bad_pages = NULL;
627 if (process->block_table) {
628 g_hash_table_foreach (process->block_table, process_free_block, NULL);
629 g_hash_table_destroy (process->block_table);
630 process->block_table = NULL;
633 if (process->stack_stash) {
634 stack_stash_unref (process->stack_stash);
635 process->stack_stash = NULL;
639 char *
640 process_find_exec (char **args)
642 int i;
644 if (g_file_test(args[0], G_FILE_TEST_EXISTS)) {
645 if (!g_path_is_absolute (args[0]))
646 return g_strconcat ("./", args[0], NULL);
647 else
648 return g_strdup (args[0]);
649 } else {
650 char **paths;
651 char *path = NULL;
652 char *pathenv = getenv ("PATH");
653 if (pathenv)
655 paths = g_strsplit (pathenv, ":", -1);
656 for (i=0; paths[i]; i++) {
657 path = g_build_filename (paths[i], args[0], NULL);
658 if (g_file_test (path, G_FILE_TEST_EXISTS))
659 break;
660 else {
661 g_free (path);
662 path = NULL;
666 g_strfreev (paths);
669 return path;
673 char **
674 process_parse_exec (const char *exec_string)
676 return g_strsplit (exec_string, " ", -1);
679 GType
680 mp_process_get_type (void)
682 static GType process_type = 0;
684 if (!process_type) {
685 static const GTypeInfo process_info = {
686 sizeof (MPProcessClass),
687 (GBaseInitFunc) NULL,
688 (GBaseFinalizeFunc) NULL,
689 (GClassInitFunc) mp_process_class_init,
690 NULL, /* class_finalize */
691 NULL, /* class_data */
692 sizeof (MPProcess),
693 0, /* n_preallocs */
694 (GInstanceInitFunc) mp_process_init
697 process_type = g_type_register_static (G_TYPE_OBJECT,
698 "MPProcess",
699 &process_info, 0);
702 return process_type;
705 static void
706 mp_process_class_init (MPProcessClass *class)
708 static gboolean initialized = FALSE;
710 GObjectClass *o_class = G_OBJECT_CLASS (class);
712 o_class->finalize = mp_process_finalize;
714 if (!initialized) {
715 process_signals[STATUS_CHANGED] =
716 g_signal_new ("status_changed",
717 MP_TYPE_PROCESS,
718 G_SIGNAL_RUN_LAST,
719 G_STRUCT_OFFSET (MPProcessClass, status_changed),
720 NULL, NULL,
721 g_cclosure_marshal_VOID__VOID,
722 G_TYPE_NONE, 0);
724 process_signals[RESET] =
725 g_signal_new ("reset",
726 MP_TYPE_PROCESS,
727 G_SIGNAL_RUN_LAST,
728 G_STRUCT_OFFSET (MPProcessClass, reset),
729 NULL, NULL,
730 g_cclosure_marshal_VOID__VOID,
731 G_TYPE_NONE, 0);
733 initialized = TRUE;
737 static void
738 mp_process_init (MPProcess *process)
740 process->status = MP_PROCESS_INIT;
741 process->pid = 0;
742 process->clone_of = NULL;
744 process->bytes_used = 0;
745 process->n_allocations = 0;
746 process->block_table = NULL;
747 process->stack_stash = NULL;
749 process->program_name = NULL;
750 process->input_channel = NULL;
752 process->seqno = 0;
754 process->command_queue = NULL;
756 process->follow_fork = FALSE;
757 process->follow_exec = FALSE;
759 g_object_ref (G_OBJECT (process));
762 static void
763 mp_process_finalize (GObject *object)
765 MPProcess *process = MP_PROCESS (object);
767 process_reinit (process);
769 g_free (process->program_name);
773 MPProcess *
774 process_new (MPServer *server)
776 MPProcess *process;
778 process = g_object_new (MP_TYPE_PROCESS, NULL);
780 process->server = server;
782 return process;
785 void
786 process_set_follow_fork (MPProcess *process,
787 gboolean follow_fork)
789 process->follow_fork = follow_fork;
792 void
793 process_set_follow_exec (MPProcess *process,
794 gboolean follow_exec)
796 process->follow_exec = follow_exec;
799 void
800 process_run (MPProcess *process, const char *path, char **args)
802 process->program_name = g_strdup (path);
803 read_inode (path);
805 process->pid = mp_server_instrument (process->server, path, args);
806 mp_server_add_process (process->server, process);
808 process_set_status (process, MP_PROCESS_STARTING);
810 process_start_input (process);
813 GList *
814 process_get_clones (MPProcess *process)
816 return mp_server_get_process_clones (process->server, process);
819 void
820 process_set_status (MPProcess *process, MPProcessStatus status)
822 if (process->status != status) {
823 process->status = status;
824 g_signal_emit_by_name (process, "status_changed", NULL);
828 char *
829 process_get_status_text (MPProcess *process)
831 char *status = "";
833 switch (process->status) {
834 case MP_PROCESS_INIT:
835 status = _("Initial");
836 break;
837 case MP_PROCESS_STARTING:
838 status = _("Starting");
839 break;
840 case MP_PROCESS_RUNNING:
841 status = _("Running");
842 break;
843 case MP_PROCESS_EXITING:
844 status = _("Exiting");
845 break;
846 case MP_PROCESS_DEFUNCT:
847 status = _("Defunct");
848 break;
849 case MP_PROCESS_DETACHED:
850 status = _("Defunct");
851 break;
854 return g_strdup (status);
857 char *
858 process_get_cmdline (MPProcess *process)
860 char *fname;
861 char *result;
862 char *tmp = NULL;
863 size_t n = 0;
864 FILE *in = NULL;
866 if (process->status == MP_PROCESS_DEFUNCT)
867 return g_strdup ("");
869 fname = g_strdup_printf ("/proc/%d/cmdline", process->pid);
870 in = fopen (fname, "r");
871 if (!in) {
872 g_warning ("Can't open %s\n", fname);
873 return g_strdup ("");
875 g_free (fname);
877 if (getline (&tmp, &n, in) == -1)
878 result = g_strdup ("");
879 else {
880 result = g_strdup (tmp);
881 free (tmp);
884 fclose (in);
886 return result;
889 void
890 process_detach (MPProcess *process)
892 int ret;
894 if (process->status != MP_PROCESS_DEFUNCT) {
895 int fd = g_io_channel_unix_get_fd (process->input_channel);
897 if (process->status == MP_PROCESS_EXITING) {
898 char response = 0;
899 ret = write (fd, &response, 1);
900 } else {
901 g_io_channel_shutdown (process->input_channel, TRUE, NULL);
902 process_set_status (process, MP_PROCESS_DETACHED);
907 void
908 process_kill (MPProcess *process)
910 if (process->status == MP_PROCESS_EXITING) {
911 process_detach (process);
912 } else if (process->status != MP_PROCESS_DEFUNCT &&
913 process->status != MP_PROCESS_INIT) {
914 kill (process->pid, SIGTERM);
918 typedef struct {
919 MPProcessBlockForeachFunc foreach_func;
920 gpointer data;
921 } BlockForeachInfo;
923 static void
924 block_table_foreach_func (gpointer key,
925 gpointer value,
926 gpointer data)
928 BlockForeachInfo *info = data;
930 info->foreach_func (value, info->data);
933 void
934 process_block_foreach (MPProcess *process,
935 MPProcessBlockForeachFunc foreach_func,
936 gpointer data)
938 GHashTable *block_table = get_block_table (process);
939 BlockForeachInfo info;
941 info.foreach_func = foreach_func;
942 info.data = data;
944 g_hash_table_foreach (block_table, block_table_foreach_func, &info);
947 gboolean
948 symbol_equal (gconstpointer s1, gconstpointer s2)
950 return s1 == s2;
953 guint
954 symbol_hash (gconstpointer s)
956 const char *symbol = s;
958 return g_str_hash (symbol);
961 char *
962 symbol_copy (const char *orig)
964 if (!orig)
965 return NULL;
967 return (char *) orig;
970 void
971 symbol_free (char *symbol)
973 if (!symbol)
974 return;