process.c: Use the symbol, it looks like it was meant to be used...
[memprof.git] / src / leakdetect.c
blob87d25d170e237736a5fd923667764809236e63c7
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 #include <sys/mman.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/ptrace.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33 #include <glib/gi18n.h>
35 #include "memprof.h"
36 #include "process.h"
38 static void
39 prepare_block (Block *block, gpointer data)
41 GPtrArray *arr = data;
43 g_assert (!(block->flags & BLOCK_IS_ROOT));
44 block->flags &= ~BLOCK_MARKED;
45 block->refcount++;
46 g_ptr_array_add (arr, block);
49 static gint
50 compare_blocks (const void *a, const void *b)
52 Block * const *b1 = a;
53 Block * const *b2 = b;
55 return ((*b1)->addr < (*b2)->addr) ? -1 :
56 ((*b1)->addr == (*b2)->addr ? 0 : 1);
59 static gboolean
60 read_proc_stat (int pid, char *status, guint *start_stack, guint *end_stack)
62 gchar *fname;
63 gulong tstart_stack;
64 gulong tend_stack;
65 char tstatus;
66 FILE *in;
68 fname = g_strdup_printf ("/proc/%d/stat", pid);
69 in = fopen (fname, "r");
70 if (!in) {
71 g_warning ("Can't open %s\n", fname);
72 return FALSE;
74 g_free (fname);
76 /* The fields in proc/stat are:
77 * pid
78 * tsk->comm
79 * state
80 * tsk->p_pptr->pid
81 * tsk->pgrp
82 * tsk->session
83 * tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0
84 * tty_pgrp
85 * tsk->flags
86 * tsk->min_flt
88 * tsk->cmin_flt
89 * tsk->maj_flt
90 * tsk->cmaj_flt
91 * tsk->utime
92 * tsk->stime
93 * tsk->cutime
94 * tsk->cstime
95 * priority
96 * nice
97 * tsk->timeout
99 * tsk->it_real_value
100 * tsk->start_time
101 * vsize
102 * tsk->mm ? tsk->mm->rss : 0
103 * tsk->rlim ? tsk->rlim[RLIMIT_RSS].rlim_cur : 0
104 * tsk->mm ? tsk->mm->start_code : 0
105 * tsk->mm ? tsk->mm->end_code : 0
106 * tsk->mm ? tsk->mm->start_stack : 0
107 * esp
108 * eip
110 * tsk->signal
111 * tsk->blocked
112 * sigignore
113 * sigcatch
114 * wchan
115 * tsk->nswap
116 * tsk->cnswap);
117 if (fscanf(in,"%*d %*s %c %*d %*d %*d %*d %*d %*lu %*lu \
118 %*lu %*lu %*lu %*lu %*lu %*ld %*ld %*ld %*ld %*ld \
119 %*ld %*lu %*lu %*ld %*lu %*lu %*lu %lu %lu %*lu \
120 %*lu %*lu %*lu %*lu %*lu %*lu %*lu",
122 if (fscanf(in,"%*d %*s %c %*d %*d %*d %*d %*d %*u %*u \
123 %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d \
124 %*d %*u %*u %*d %*u %*u %*u %lu %lu %*u \
125 %*u %*u %*u %*u %*u %*u %*u",
126 &tstatus,
127 &tstart_stack,
128 &tend_stack) != 3) {
129 g_warning ("Error parsing /proc/%d/stat\n", pid);
130 return FALSE;
133 if (status)
134 *status = tstatus;
135 if (start_stack)
136 *start_stack = tstart_stack;
137 if (end_stack)
138 *end_stack = tend_stack;
140 fclose (in);
142 return TRUE;
145 static char
146 process_status (int pid)
148 char c = 'T'; /* Avoid infinite loop in caller */
150 read_proc_stat (pid, &c, NULL, NULL);
152 return c;
155 /* FIXME: merge with read_process_maps */
156 static GList *
157 read_stack_maps (MPProcess *process)
159 GList *result = NULL;
160 gchar buffer[1024];
161 FILE *in;
162 gchar perms[26];
163 gchar file[256];
164 guint start, end, major, minor, inode;
166 snprintf (buffer, 1023, "/proc/%d/maps", process->pid);
168 in = fopen (buffer, "r");
169 if (!in)
170 return NULL;
172 while (fgets(buffer, 1023, in)) {
173 int count = sscanf (buffer, "%x-%x %15s %*x %x:%x %u %255s",
174 &start, &end, perms, &major, &minor, &inode, file);
175 if (count >= 6) {
176 if (strcmp (perms, "rwxp") == 0) {
177 Map *map;
179 map = g_new (Map, 1);
180 map->start = start;
181 map->size = end - start;
182 map->name = NULL;
183 map->offset = 0;
185 result = g_list_prepend (result, map);
190 fclose (in);
192 return result;
195 static GSList *
196 add_stack_root (MPProcess *process, GSList *block_list,
197 GList *map_list)
199 GList *tmp_list;
200 guint start_stack, end_stack;
202 tmp_list = map_list;
204 if (!read_proc_stat (process->pid, NULL, &start_stack, &end_stack))
205 return block_list;
207 while (tmp_list) {
208 Map *map = tmp_list->data;
210 if (end_stack >= map->start &&
211 end_stack < map->start + map->size) {
212 Block *block;
214 block = g_new (Block, 1);
215 block->refcount = 1;
216 block->flags = BLOCK_IS_ROOT;
217 block->addr = (void *)end_stack;
218 if (start_stack > map->start &&
219 start_stack < map->start + map->size)
220 block->size = start_stack - end_stack;
221 else
222 block->size = map->start + map->size - end_stack;
223 block->stack = NULL;
225 return g_slist_prepend (block_list, block);
228 tmp_list = tmp_list->next;
231 return block_list;
234 static GSList *
235 add_stack_roots (MPProcess *process, GSList *block_list)
237 GList *clones, *tmp_list;
238 GList *stack_maps;
240 clones = process_get_clones (process);
241 stack_maps = read_stack_maps (process);
243 tmp_list = clones;
244 while (tmp_list) {
245 block_list = add_stack_root (tmp_list->data, block_list, stack_maps);
246 tmp_list = tmp_list->next;
248 g_list_free (clones);
250 g_list_foreach (stack_maps, (GFunc)g_free, NULL);
251 g_list_free (stack_maps);
253 return block_list;
256 void
257 process_data_root (void *addr, guint size, gpointer user_data)
259 GSList **block_list = user_data;
260 Block *block;
262 block = g_new (Block, 1);
263 block->refcount = 1;
264 block->flags = BLOCK_IS_ROOT;
265 block->addr = addr;
266 block->size = size;
267 block->stack = NULL;
269 *block_list = g_slist_prepend (*block_list, block);
272 static GSList *
273 add_data_roots (MPProcess *process, GSList *block_list)
275 process_sections (process, process_data_root, &block_list);
277 return block_list;
280 static Block *
281 find_block (GPtrArray *block_arr, void *addr)
283 Block **data;
284 guint first, middle, last;
286 if (block_arr->len == 0)
287 return NULL;
289 first = 0;
290 last = block_arr->len - 1;
291 middle = last;
293 data = (Block **)block_arr->pdata;
295 if (addr < data[first]->addr) {
296 return NULL;
298 else if (addr < data[last]->addr) {
299 /* Invariant: data[first].addr <= val < data[last].addr */
301 while (first < last - 1) {
302 middle = (first + last) / 2;
303 if (addr < data[middle]->addr)
304 last = middle;
305 else
306 first = middle;
308 if (addr < data[first]->addr + data[first]->size)
309 return data[first];
310 else
311 return NULL;
312 } else {
313 if (addr < data[last]->addr + data[last]->size)
314 return data[last];
315 else
316 return NULL;
320 static GSList *
321 scan_block_contents (GPtrArray *block_arr,
322 GSList *block_list,
323 Block *block, void **mem)
325 int i;
327 for (i=0; i < block->size / sizeof(void *); i++) {
328 Block *new_block;
330 new_block = find_block (block_arr, mem[i]);
332 g_assert (!new_block || !(new_block->flags & BLOCK_IS_ROOT));
334 if (new_block && !(new_block->flags & BLOCK_MARKED)) {
335 block_list = g_slist_prepend (block_list, new_block);
336 new_block->flags |= BLOCK_MARKED;
340 return block_list;
343 static GSList *
344 scan_block (pid_t pid, int memfd, GSList *block_list,
345 GPtrArray *block_arr, Block *block)
347 void **mem;
348 gint i;
349 void *addr;
350 size_t length = (block->size + 3) / 4;
352 addr = g_new (void *, length);
353 mem = (void **)addr;
355 for (i = 0; i < length; i++) {
356 mem[i] = (void *)ptrace (PTRACE_PEEKDATA, pid,
357 block->addr+i*sizeof(void *),
358 &mem[i]);
359 if (errno)
361 g_warning ("Cannot read word %d/%d in block %p: %s\n",
362 i, length, block->addr, g_strerror (errno));
363 g_free (addr);
364 return block_list;
368 block_list = scan_block_contents (block_arr, block_list,
369 block, mem);
371 g_free (addr);
372 return block_list;
375 GSList *
376 leaks_find (MPProcess *process)
378 int i;
379 GPtrArray *block_arr;
380 GSList *block_list = NULL;
381 GSList *result = NULL;
382 int memfd;
383 gchar *fname;
384 GList *clones, *tmp_list;
386 clones = process_get_clones (process);
388 ptrace (PTRACE_ATTACH, process->pid, 0, 0);
390 tmp_list = clones;
391 while (tmp_list) {
392 MPProcess *clone = tmp_list->data;
394 kill (clone->pid, SIGSTOP);
396 /* Wait for the processes we are tracing to actually stop */
398 /* waitpid(clone->pid, &status, WUNTRACED); */
399 while (process_status (clone->pid) != 'T') {
400 usleep(50000);
403 tmp_list = tmp_list->next;
406 fname = g_strdup_printf ("/proc/%d/mem", process->pid);
407 memfd = open (fname, O_RDONLY);
408 if (memfd < 0) {
409 g_warning ("Can't open %s\n", fname);
410 return NULL;
413 g_free (fname);
415 /* Mark all blocks as untouched, add them to list of blocks
418 block_arr = g_ptr_array_new ();
419 process_block_foreach (process, prepare_block, block_arr);
421 qsort (block_arr->pdata, block_arr->len, sizeof (Block *),
422 compare_blocks);
424 /* Locate all the roots
427 block_list = add_stack_roots (process, block_list);
428 block_list = add_data_roots (process, block_list);
430 /* While there are blocks to check, scan each block,
431 * and add each new-found block to the global list.
434 while (block_list) {
435 GSList *tmp_list;
436 Block *block = block_list->data;
438 tmp_list = block_list->next;
439 g_slist_free_1 (block_list);
440 block_list = tmp_list;
442 block_list = scan_block (process->pid, memfd, block_list, block_arr, block);
444 #if 0
445 if (block->flags & BLOCK_IS_ROOT)
446 g_free (block);
447 #endif
450 close (memfd);
452 /* Look for leaked blocks
455 for (i=0; i<block_arr->len; i++) {
456 Block *block = block_arr->pdata[i];
457 if (!(block->flags & BLOCK_MARKED)) {
458 result = g_slist_prepend (result, block);
459 } else {
460 block_unref(block);
464 tmp_list = clones;
465 while (tmp_list) {
466 MPProcess *clone = tmp_list->data;
468 kill (clone->pid, SIGCONT);
470 tmp_list = tmp_list->next;
473 /* Clean up
475 g_list_free (clones);
477 g_ptr_array_free (block_arr, FALSE);
479 close (memfd);
480 if (ptrace (PTRACE_DETACH, process->pid, 0, 0) == -1)
481 g_warning ("Detach failed %s (%d)\n", g_strerror (errno), errno);
483 return result;
486 void
487 leaks_print (MPProcess *process, GSList *blocks, gchar *outfile)
489 GSList *tmp_list = blocks;
490 FILE *out;
492 out = fopen (outfile, "w");
493 if (!out)
495 show_error (NULL, ERROR_MODAL,
496 _("Cannot open output file: %s\n"),
497 g_strerror (errno));
498 return;
501 while (tmp_list)
503 Block *block = tmp_list->data;
505 fprintf (out, "Leaked %p (%u bytes)\n", block->addr, block->size);
506 process_dump_stack (process, out, block->stack);
508 tmp_list = tmp_list->next;
511 fclose (out);