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.
30 #include <sys/types.h>
31 #include <sys/socket.h>
34 #include <sys/sysmacros.h>
36 #include <glib-object.h>
38 #include "memintercept.h"
42 #include <glib/gi18n.h>
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
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));
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
);
86 unqueue_command (MPProcess
*process
, MIInfo
*info
, StackNode
**stack
)
88 Command
*command
= process
->command_queue
->data
;
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
);
98 g_list_free_1 (tmp_list
);
105 /************************************************************
107 ************************************************************/
110 block_unref (Block
*block
)
113 if (block
->refcount
== 0)
117 /************************************************************
118 * Code to map addresses to object files
119 ************************************************************/
127 prepare_map (Map
*map
)
129 g_return_if_fail (!map
->prepared
);
131 map
->binfile
= bin_file_new (map
->name
);
133 map
->prepared
= TRUE
;
137 process_read_maps (MPProcess
*process
)
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
)) {
162 buffer
, "%lx-%lx %*15s %lx %*x:%*x %lu %255s",
163 &start
, &end
, &offset
, &inode
, file
);
168 map
= g_new (Map
, 1);
169 map
->prepared
= FALSE
;
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
);
185 real_locate_map (MPProcess
*process
, guint addr
)
187 GList
*tmp_list
= process
->map_list
;
191 Map
*tmp_map
= tmp_list
->data
;
193 if ((addr
>= tmp_map
->start
) &&
194 (addr
< tmp_map
->start
+ tmp_map
->size
))
197 tmp_list
= tmp_list
->next
;
204 locate_map (MPProcess
*process
, guint addr
)
206 Map
*map
= real_locate_map (process
, addr
);
209 gpointer page_addr
= (gpointer
) (addr
- addr
% MP_PAGE_SIZE
);
210 if (g_list_find (process
->bad_pages
, page_addr
))
213 process_read_maps (process
);
215 map
= real_locate_map (process
, addr
);
218 process
->bad_pages
= g_list_prepend (process
->bad_pages
, page_addr
);
230 process_locate_symbol (MPProcess
*process
, guint addr
)
232 Map
*map
= locate_map (process
, addr
);
233 const BinSymbol
*symbol
;
241 symbol
= bin_file_lookup_symbol (map
->binfile
, addr
);
243 return bin_symbol_get_name (map
->binfile
, symbol
);
247 process_find_line (MPProcess
*process
, void *address
,
248 const char **filename
, char **functionname
,
251 const char *s
= process_locate_symbol (process
, address
);
256 *functionname
= (char*)s
;
264 process_dump_stack (MPProcess
*process
, FILE *out
, StackNode
*stack
)
266 for (; stack
!= NULL
; stack
= stack
->parent
)
268 const char *filename
;
272 if (process_find_line (process
, stack
->address
,
273 &filename
, &functionname
, &line
)) {
275 fprintf(out
, "\t%s(): %s:%u\n", functionname
, filename
, line
);
277 fprintf(out
, "\t%s()\n", functionname
);
279 fprintf(out
, "\t[%p]\n", stack
->address
);
285 process_map_sections (Map
*map
, SectionFunc func
, gpointer user_data
)
287 /* FIXME: this should be reinstated */
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
;
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);
311 process_sections (MPProcess
*process
, SectionFunc func
, gpointer user_data
)
315 process_read_maps (process
);
317 tmp_list
= process
->map_list
;
320 Map
*map
= tmp_list
->data
;
325 process_map_sections (map
, func
, user_data
);
326 tmp_list
= tmp_list
->next
;
330 /************************************************************
331 * Communication with subprocess
332 ************************************************************/
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
;
345 process_free_block (gpointer key
, gpointer value
, gpointer data
)
351 process_duplicate_block (gpointer key
, gpointer value
, gpointer data
)
353 GHashTable
*new_table
= data
;
354 Block
*block
= value
;
358 g_hash_table_insert (new_table
, key
, value
);
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
,
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
;
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;
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
;
421 process
->command_queue
= NULL
;
425 get_stack_stash (MPProcess
*process
)
427 if (!process
->stack_stash
)
428 process
->stack_stash
= stack_stash_new (NULL
);
430 return process
->stack_stash
;
434 process_exec_reset (MPProcess
*process
)
436 process_reinit (process
);
437 g_signal_emit_by_name (process
, "reset");
441 process_command (MPProcess
*process
, MIInfo
*info
, StackNode
*stack
)
443 GHashTable
*block_table
;
446 if (info
->any
.seqno
!= process
->seqno
) {
447 queue_command (process
, info
, stack
);
455 switch (info
->operation
) {
460 g_assert_not_reached ();
464 /* Handled before, ignore */
468 info
->alloc
.old_ptr
= NULL
;
469 info
->alloc
.new_ptr
= (void *)process
->seqno
;
470 info
->alloc
.size
= 1;
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
);
479 g_warning ("Block %p not found (pid=%d)!\n", info
->alloc
.old_ptr
, process
->pid
);
480 process_dump_stack (process
, stderr
, stack
);
483 g_hash_table_remove (block_table
, info
->alloc
.old_ptr
);
484 process
->bytes_used
-= block
->size
;
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);
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
);
520 input_func (GIOChannel
*source
,
521 GIOCondition condition
,
526 MPProcess
*input_process
= data
;
527 MPProcess
*process
= NULL
;
530 memset (&info
, 0, sizeof (info
));
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
);
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
);
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
));
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;
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
,
603 G_IO_IN
| G_IO_PRI
| G_IO_ERR
| G_IO_HUP
| G_IO_NVAL
,
604 input_func
, process
, NULL
);
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
;
640 process_find_exec (char **args
)
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
);
648 return g_strdup (args
[0]);
652 char *pathenv
= getenv ("PATH");
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
))
674 process_parse_exec (const char *exec_string
)
676 return g_strsplit (exec_string
, " ", -1);
680 mp_process_get_type (void)
682 static GType process_type
= 0;
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 */
694 (GInstanceInitFunc
) mp_process_init
697 process_type
= g_type_register_static (G_TYPE_OBJECT
,
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
;
715 process_signals
[STATUS_CHANGED
] =
716 g_signal_new ("status_changed",
719 G_STRUCT_OFFSET (MPProcessClass
, status_changed
),
721 g_cclosure_marshal_VOID__VOID
,
724 process_signals
[RESET
] =
725 g_signal_new ("reset",
728 G_STRUCT_OFFSET (MPProcessClass
, reset
),
730 g_cclosure_marshal_VOID__VOID
,
738 mp_process_init (MPProcess
*process
)
740 process
->status
= MP_PROCESS_INIT
;
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
;
754 process
->command_queue
= NULL
;
756 process
->follow_fork
= FALSE
;
757 process
->follow_exec
= FALSE
;
759 g_object_ref (G_OBJECT (process
));
763 mp_process_finalize (GObject
*object
)
765 MPProcess
*process
= MP_PROCESS (object
);
767 process_reinit (process
);
769 g_free (process
->program_name
);
774 process_new (MPServer
*server
)
778 process
= g_object_new (MP_TYPE_PROCESS
, NULL
);
780 process
->server
= server
;
786 process_set_follow_fork (MPProcess
*process
,
787 gboolean follow_fork
)
789 process
->follow_fork
= follow_fork
;
793 process_set_follow_exec (MPProcess
*process
,
794 gboolean follow_exec
)
796 process
->follow_exec
= follow_exec
;
800 process_run (MPProcess
*process
, const char *path
, char **args
)
802 process
->program_name
= g_strdup (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
);
814 process_get_clones (MPProcess
*process
)
816 return mp_server_get_process_clones (process
->server
, process
);
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
);
829 process_get_status_text (MPProcess
*process
)
833 switch (process
->status
) {
834 case MP_PROCESS_INIT
:
835 status
= _("Initial");
837 case MP_PROCESS_STARTING
:
838 status
= _("Starting");
840 case MP_PROCESS_RUNNING
:
841 status
= _("Running");
843 case MP_PROCESS_EXITING
:
844 status
= _("Exiting");
846 case MP_PROCESS_DEFUNCT
:
847 status
= _("Defunct");
849 case MP_PROCESS_DETACHED
:
850 status
= _("Defunct");
854 return g_strdup (status
);
858 process_get_cmdline (MPProcess
*process
)
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");
872 g_warning ("Can't open %s\n", fname
);
873 return g_strdup ("");
877 if (getline (&tmp
, &n
, in
) == -1)
878 result
= g_strdup ("");
880 result
= g_strdup (tmp
);
890 process_detach (MPProcess
*process
)
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
) {
899 ret
= write (fd
, &response
, 1);
901 g_io_channel_shutdown (process
->input_channel
, TRUE
, NULL
);
902 process_set_status (process
, MP_PROCESS_DETACHED
);
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
);
919 MPProcessBlockForeachFunc foreach_func
;
924 block_table_foreach_func (gpointer key
,
928 BlockForeachInfo
*info
= data
;
930 info
->foreach_func (value
, info
->data
);
934 process_block_foreach (MPProcess
*process
,
935 MPProcessBlockForeachFunc foreach_func
,
938 GHashTable
*block_table
= get_block_table (process
);
939 BlockForeachInfo info
;
941 info
.foreach_func
= foreach_func
;
944 g_hash_table_foreach (block_table
, block_table_foreach_func
, &info
);
948 symbol_equal (gconstpointer s1
, gconstpointer s2
)
954 symbol_hash (gconstpointer s
)
956 const char *symbol
= s
;
958 return g_str_hash (symbol
);
962 symbol_copy (const char *orig
)
967 return (char *) orig
;
971 symbol_free (char *symbol
)