home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!stanford.edu!agate!spool.mu.edu!nigel.msen.com!emory!swrinde!cs.utexas.edu!sun-barr!news2me.EBay.Sun.COM!cronkite.Central.Sun.COM!texsun!exucom.exu.ericsson.se!news
- From: ebubra@ebu.ericsson.se (Bertil Askelid)
- Newsgroups: gnu.gcc.help
- Subject: Re: Memory leak detector with gdb?
- Message-ID: <1992Nov9.193300.25504@exu.ericsson.se>
- Date: 9 Nov 92 19:33:00 GMT
- Sender: news@exu.ericsson.se
- Reply-To: ebubra@ebu.ericsson.se (Bertil Askelid)
- Followup-To: gnu.gdb.bug
- Organization: Ericsson Business Communications, Anaheim, California
- Lines: 620
- Nntp-Posting-Host: proton.ebu.ericsson.se
- X-Disclaimer: This article was posted by a user at Ericsson.
- Any opinions expressed are strictly those of the
- user and not necessarily those of Ericsson.
-
- <me> A while ago an article was posted about a memory leak detector
- <me> running with gdb 4.*. I missed it. Is there someone who happens to
- <me> have a copy of it and would like to email it to me?
- <me>
- <me> Thanks!
-
- Thanks to Jan-Erik.Stromquist@uppsala.telesoft.se and
- Patrik.Carlsson@eua.ericsson.se I got hold of the article I asked
- for. As I have received a couple of requests from other persons also
- looking for this gdb memory leak detector patch, I repost it:
-
- ================================================================================
- Newsgroups: gnu.gdb.bug
- From: L.Dachary@cs.ucl.ac.uk
- Subject: patch for gdb-4.6 to trace memory leaks
- Organization: GNUs Not Usenet
- Distribution: gnu
- Date: Thu, 15 Oct 1992 17:38:42 GMT
-
- This is a patch for gdb-4.6. It introduces two commands to
- trace memory leaks.
- The leaks_trace command sets a breakpoint in the malloc
- function and one in the free function. There is one action associated
- with each breakpoint. The action associated with the malloc function
- stores the backtrace of function calls using the address of the newly
- created object as an index. The action associated with the free
- function forgets the backtrace associated with the address of the
- object being deleted.
- The leaks_print command prints the table containing backtraces
- associated with object addresses.
- If a program has no memory leaks the table should be empty
- when the program terminates. If it has memory leaks the backtrace
- allows the user to find the place where the non-deleted object has
- been created.
-
- In order to use this facility the executable file must contain
- a version of malloc compiled with debugging information. Any malloc
- package may be used.
-
- Here is a session record that illustrate how this patch can be
- used.
-
- ---- source b.c -----------
- 1 main()
- 2 {
- 3 char* a = (char*)malloc(1);
- 4 free(a);
- 5 a = (char*)malloc(1);
- 6 }
- ---------------------------
- $ gcc -g -o b b.c malloc.o
- $ gdb b
- GDB is free software and you are welcome to distribute copies of it
- under certain conditions; type "show copying" to see the conditions.
- There is absolutely no warranty for GDB; type "show warranty" for details.
- GDB 4.6, Copyright 1992 Free Software Foundation, Inc...
- (gdb) quit
- $ gcc -g -o b b.c malloc.o
- $ gdb b
- GDB is free software and you are welcome to distribute copies of it
- under certain conditions; type "show copying" to see the conditions.
- There is absolutely no warranty for GDB; type "show warranty" for details.
- GDB 4.6, Copyright 1992 Free Software Foundation, Inc...
- (gdb) leaks_trace
- Breakpoint for malloc
- Breakpoint 1 at 0x298c: file malloc.c, line 481.
- Breakpoint for free
- Breakpoint 2 at 0x2998: file malloc.c, line 488.
- (gdb) run
- Starting program: /tmp_mnt/cs/research/coside/fsfb/croissant/coside/gdb-4.6/gdb/b
-
- Program exited with code 01.
- (gdb) leaks_print
- Backtrace for 0x4c18
- #0 0x22c4 in main () at b.c:5
- (gdb) quit
- $
-
- I will not maintain this. Please do not send mail concerning
- problems you may have with this patch.
-
- Have fun,
-
- Loic
-
- BUGS: Only 20 function calls per object.
- Tested only on a sparc.
- Slows down the debugged program dramaticaly.
- Eats a lot of memory.
- ---------------------------------------------------------------------------------
- *** ../../gdb-4.6-orig/gdb/breakpoint.c Mon Jul 6 18:10:37 1992
- --- breakpoint.c Tue Oct 13 13:55:49 1992
- ***************
- *** 157,163 ****
-
- /* Number of last breakpoint made. */
-
- ! static int breakpoint_count;
-
- /* Set breakpoint count to NUM. */
- static void
- --- 157,163 ----
-
- /* Number of last breakpoint made. */
-
- ! int breakpoint_count;
-
- /* Set breakpoint count to NUM. */
- static void
- *** ../../gdb-4.6-orig/gdb/infcmd.c Mon Jul 6 18:10:52 1992
- --- infcmd.c Thu Oct 15 15:51:23 1992
- ***************
- *** 31,37 ****
- #include "gdbcore.h"
- #include "target.h"
-
- ! static void
- continue_command PARAMS ((char *, int));
-
- static void
- --- 31,37 ----
- #include "gdbcore.h"
- #include "target.h"
-
- ! void
- continue_command PARAMS ((char *, int));
-
- static void
- ***************
- *** 94,100 ****
- static void
- nexti_command PARAMS ((char *, int));
-
- ! static void
- stepi_command PARAMS ((char *, int));
-
- static void
- --- 94,100 ----
- static void
- nexti_command PARAMS ((char *, int));
-
- ! void
- stepi_command PARAMS ((char *, int));
-
- static void
- ***************
- *** 244,250 ****
- environ_vector (inferior_environ));
- }
-
- ! static void
- continue_command (proc_count_exp, from_tty)
- char *proc_count_exp;
- int from_tty;
- --- 244,250 ----
- environ_vector (inferior_environ));
- }
-
- ! void
- continue_command (proc_count_exp, from_tty)
- char *proc_count_exp;
- int from_tty;
- ***************
- *** 308,314 ****
- /* Likewise, but step only one instruction. */
-
- /* ARGSUSED */
- ! static void
- stepi_command (count_string, from_tty)
- char *count_string;
- int from_tty;
- --- 308,314 ----
- /* Likewise, but step only one instruction. */
-
- /* ARGSUSED */
- ! void
- stepi_command (count_string, from_tty)
- char *count_string;
- int from_tty;
- *** ../../gdb-4.6-orig/gdb/stack.c Mon Jul 6 18:28:50 1992
- --- stack.c Thu Oct 15 15:22:31 1992
- ***************
- *** 129,134 ****
- --- 129,136 ----
- print_frame_info (fi, level, source, 1);
- }
-
- + static int malloc_silent = 0;
- +
- void
- print_frame_info (fi, level, source, args)
- struct frame_info *fi;
- ***************
- *** 211,217 ****
- funname = msymbol -> name;
- }
-
- ! if (source >= 0 || !sal.symtab)
- {
- if (level >= 0)
- printf_filtered ("#%-2d ", level);
- --- 213,219 ----
- funname = msymbol -> name;
- }
-
- ! if (malloc_silent == 0 && (source >= 0 || !sal.symtab))
- {
- if (level >= 0)
- printf_filtered ("#%-2d ", level);
- ***************
- *** 245,251 ****
- printf_filtered ("\n");
- }
-
- ! if ((source != 0) && sal.symtab)
- {
- int done = 0;
- int mid_statement = source < 0 && fi->pc != sal.pc;
- --- 247,253 ----
- printf_filtered ("\n");
- }
-
- ! if (malloc_silent == 0 && (source != 0) && sal.symtab)
- {
- int done = 0;
- int mid_statement = source < 0 && fi->pc != sal.pc;
- ***************
- *** 551,556 ****
- --- 553,925 ----
- }
- #endif
-
- + typedef char bool;
- + typedef int leak_pointer;
- +
- + #define TRUE ((bool)1)
- + #define FALSE ((bool)0)
- +
- + #define LEAKS_PC_MAX 20
- + typedef struct leak_trace {
- + CORE_ADDR pcs[LEAKS_PC_MAX];
- + bool valid;
- + char index;
- + long address;
- + leak_pointer next;
- + } leak_trace;
- +
- + #define LEAKS_TABLE_SIZE 1031
- + leak_pointer leaks_table[LEAKS_TABLE_SIZE];
- + #define LEAKS_POOL_INITIAL 1000
- + #define LEAKS_POOL_INCR 5000
- + leak_trace* leaks_pool = 0;
- + int leaks_pool_first = 0;
- + int leaks_pool_max = 0;
- + #define LEAKS_FREE_MAX 500
- + leak_pointer leaks_free[LEAKS_FREE_MAX];
- + int leaks_free_top = 0;
- +
- + #define leaks_hash(addr) ((addr >> 1) % LEAKS_TABLE_SIZE)
- +
- + leak_pointer leak_alloc()
- + {
- + leak_pointer leak;
- + if(leaks_free_top > 0)
- + leak = leaks_free[--leaks_free_top];
- + else {
- + while(leaks_pool_first < leaks_pool_max && leaks_pool[leaks_pool_first].valid == TRUE)
- + leaks_pool_first++;
- + if(leaks_pool_first >= leaks_pool_max) {
- + int i;
- + if(leaks_pool == 0) {
- + leaks_pool = (leak_trace*)malloc(sizeof(leak_trace)*LEAKS_POOL_INITIAL);
- + leaks_pool_max = LEAKS_POOL_INITIAL;
- + for(i = 0; i < leaks_pool_max; i++)
- + leaks_pool[i].valid = FALSE;
- + } else {
- + leaks_pool = (leak_trace*)realloc(leaks_pool, sizeof(leak_trace)*(leaks_pool_max+LEAKS_POOL_INCR));
- + for(i = leaks_pool_max; i < leaks_pool_max+LEAKS_POOL_INCR; i++)
- + leaks_pool[i].valid = FALSE;
- + leaks_pool_max += LEAKS_POOL_INCR;
- + }
- + }
- + leak = leaks_pool_first++;
- + }
- + leaks_pool[leak].valid = TRUE;
- + return leak;
- + }
- +
- + void leak_free(leak)
- + leak_pointer leak;
- + {
- + leaks_pool[leak].valid = FALSE;
- + if(leaks_free_top < LEAKS_FREE_MAX)
- + leaks_free[leaks_free_top++] = leak;
- + else if(leaks_pool_first > leak)
- + leaks_pool_first = leak;
- + }
- +
- + void leaks_init()
- + {
- + int i;
- + for(i = 0; i < LEAKS_TABLE_SIZE; i++)
- + leaks_table[i] = -1;
- + for(i = 0; i < leaks_pool_max; i++)
- + leaks_pool[i].valid = FALSE;
- + leaks_pool_first = 0;
- + leaks_free_top = 0;
- + }
- +
- + void leaks_hash_store(leak)
- + leak_pointer leak;
- + {
- + long key = leaks_hash(leaks_pool[leak].address);
- + leak_pointer chain = leaks_table[key];
- + if(chain < 0) {
- + leaks_table[key] = leak;
- + } else {
- + leak_pointer previous;
- + while(chain >= 0) {
- + previous = chain;
- + chain = leaks_pool[chain].next;
- + }
- + leaks_pool[previous].next = leak;
- + }
- + leaks_pool[leak].next = -1;
- + }
- +
- + #if 0
- + leak_pointer leaks_hash_restore(address)
- + long address;
- + {
- + long key = leaks_hash(address);
- + leak_pointer chain = leaks_table[key];
- + while(chaine >= 0)
- + chaine = leaks_pool[chain].next;
- + return chain;
- + }
- + #endif
- +
- + leak_pointer leaks_hash_remove(address)
- + long address;
- + {
- + long key = leaks_hash(address);
- + leak_pointer chain = leaks_table[key];
- + if(chain >= 0) {
- + if(leaks_pool[chain].address == address) {
- + leaks_table[key] = -1;
- + } else {
- + leak_pointer previous;
- + while(chain >= 0 && leaks_pool[chain].address != address) {
- + previous = chain;
- + chain = leaks_pool[chain].next;
- + }
- + if(chain >= 0)
- + leaks_pool[previous].next = leaks_pool[chain].next;
- + }
- + }
- + return chain;
- + }
- +
- + static void
- + leaks_trace_command (dumb, from_tty)
- + char *dumb;
- + int from_tty;
- + {
- + struct expression* expr;
- + value val;
- + long address;
- + struct block* b;
- + static int bp_set = 0;
- +
- + if(bp_set == 0) {
- + if(expr = parse_expression ("&malloc")) {
- +
- + val = evaluate_expression (expr);
- + address = *(long*)VALUE_CONTENTS_RAW(val);
- +
- + if(b = block_for_pc(address)) {
- + extern struct breakpoint *breakpoint_chain;
- + extern int breakpoint_count;
- + long end = b->endaddr;
- + long lasti = address;
- + FILE* f = fopen("/dev/null", "w");
- + if(f) {
- + long curi = address;
- + char buf[512];
- + while((curi += print_insn(curi, f)) < end)
- + lasti = curi;
- + fclose(f);
- +
- + sprintf(buf, "*%d", lasti);
- + printf_filtered("Breakpoint for malloc\n");
- + break_command(buf, 0);
- + {
- + struct breakpoint* bp;
- + for (bp = breakpoint_chain; bp; bp = bp->next)
- + if(bp->number == breakpoint_count) {
- + struct command_line *first;
- + struct command_line *second;
- + first = (struct command_line*)malloc(sizeof(struct command_line));
- + second = (struct command_line*)malloc(sizeof(struct command_line));
- + first->line = savestring("silent", strlen("silent") + 1);
- + first->next = second;
- + second->line = savestring("leaks_malloc", strlen("leaks_malloc") + 1);
- + second->next = (struct command_line*)0;
- + bp->commands = first;
- + }
- + }
- + printf_filtered("Breakpoint for free\n");
- + break_command("free", 0);
- + {
- + struct breakpoint* bp;
- + for (bp = breakpoint_chain; bp; bp = bp->next)
- + if(bp->number == breakpoint_count) {
- + struct command_line *first;
- + struct command_line *second;
- + first = (struct command_line*)malloc(sizeof(struct command_line));
- + second = (struct command_line*)malloc(sizeof(struct command_line));
- + first->line = savestring("silent", strlen("silent") + 1);
- + first->next = second;
- + second->line = savestring("leaks_free", strlen("leaks_free") + 1);
- + second->next = (struct command_line*)0;
- + bp->commands = first;
- + }
- + }
- + }
- + }
- + bp_set = 1;
- + }
- + }
- + leaks_init();
- + }
- +
- + static void
- + leaks_malloc_command (allocated, from_tty)
- + char *allocated;
- + int from_tty;
- + {
- + struct frame_info *fi;
- + register int count = LEAKS_PC_MAX;
- + register FRAME frame;
- + register int i;
- + register FRAME trailing;
- + register int trailing_level;
- + value val;
- + long address;
- + char stop_registers[REGISTER_BYTES];
- + struct type value_type;
- + leak_pointer leak;
- + leak_trace* leak_tracep;
- +
- + malloc_silent = 1;
- + stepi_command (0, 0); /* Effectively return from the malloc function */
- + malloc_silent = 0;
- + memset(&value_type, '\0', sizeof(struct type));
- + read_register_bytes (0, stop_registers, REGISTER_BYTES);
- + value_type.code = TYPE_CODE_INT;
- + value_type.length = 4;
- + val = value_being_returned (&value_type, stop_registers, 0);
- + address = *(long*)VALUE_CONTENTS_RAW(val);
- +
- + #if 0
- + printf_filtered("malloc 0x%x\n", address);
- + val = access_value_history (0);
- + v = *(long*)VALUE_CONTENTS_RAW(val);
- + #endif
- +
- + leak = leak_alloc();
- + leak_tracep = &leaks_pool[leak];
- + leak_tracep->address = address;
- + trailing = get_current_frame ();
- + for (i = 0, frame = trailing;
- + frame && count--;
- + i++, frame = get_prev_frame (frame))
- + {
- + QUIT;
- + leak_tracep->pcs[i] = get_frame_info (frame)->pc;
- + }
- + leak_tracep->index = i;
- + leaks_hash_store(leak);
- + continue_command(0, 0);
- + }
- +
- + static void
- + leaks_free_command (dumb, from_tty)
- + char *dumb;
- + int from_tty;
- + {
- + long address;
- + value val;
- + int i;
- + struct symbol* func;
- + struct symbol* sym;
- + struct block* b;
- + int nsyms;
- + FRAME fi;
- +
- + fi = get_current_frame ();
- + if(func = find_pc_function (fi->pc)) {
- + int found = 0;
- + b = SYMBOL_BLOCK_VALUE(func);
- + nsyms = BLOCK_NSYMS(b);
- + for (i = 0; !found && i < nsyms; i++)
- + {
- + QUIT;
- + sym = BLOCK_SYM (b, i);
- + switch(SYMBOL_CLASS(sym)) {
- + case LOC_ARG:
- + case LOC_REF_ARG:
- + case LOC_REGPARM:
- + case LOC_LOCAL_ARG:
- + found = 1;
- + break;
- + default:
- + break;
- + }
- + }
- + if(found) {
- + sym = lookup_symbol (SYMBOL_NAME (sym),
- + b, VAR_NAMESPACE, (int *)NULL, (struct symtab **)NULL);
- + val = read_var_value (sym, FRAME_INFO_ID (fi));
- + address = *(long*)VALUE_CONTENTS(val);
- + }
- + }
- +
- + #if 0
- + printf_filtered("free 0x%x", address);
- + #endif
- + {
- + leak_pointer leak = leaks_hash_remove(address);
- + if(leak >= 0) {
- + #if 0
- + printf_filtered(".\n");
- + #endif
- + leak_free(leak);
- + } else {
- + #if 0
- + printf_filtered("\n");
- + #endif
- + }
- + }
- + continue_command(0, 0);
- + }
- +
- + static void
- + leaks_print_command (dumb, from_tty)
- + char *dumb;
- + int from_tty;
- + {
- + int i, j;
- + static long global_ctor_lo = 0;
- + static long global_ctor_hi = 0;
- + if(global_ctor_lo == 0) {
- + long global_ctor_address;
- + struct minimal_symbol* msymbol;
- + msymbol = lookup_minimal_symbol("__do_global_ctors", 0);
- + if(msymbol) {
- + struct expression* expr = parse_expression ("&__do_global_ctors");
- + if(expr) {
- + value val = evaluate_expression (expr);
- + struct block* b;
- + global_ctor_address = *(long*)VALUE_CONTENTS_RAW(val);
- + b = block_for_pc(global_ctor_address);
- + if(b) {
- + global_ctor_lo = b->startaddr;
- + global_ctor_hi = b->endaddr;
- + }
- + }
- + }
- + }
- + if(global_ctor_lo != 0) {
- + for(i = 0; i < leaks_pool_max; i++)
- + if(leaks_pool[i].valid == TRUE) {
- + leak_trace* leak_tracep = &leaks_pool[i];
- + for(j = 0; j < leak_tracep->index; j++) {
- + long pc = leak_tracep->pcs[j];
- + if(global_ctor_lo < pc && global_ctor_hi > pc) {
- + leak_tracep->valid = FALSE;
- + break;
- + }
- + }
- + }
- + }
- + {
- + struct frame_info fi;
- + for(i = 0; i < leaks_pool_max; i++)
- + if(leaks_pool[i].valid == TRUE) {
- + leak_trace* leak_tracep = &leaks_pool[i];
- + printf_filtered("Backtrace for 0x%x\n", leak_tracep->address);
- + fi.next_frame = (FRAME_ADDR)0;
- + for(j = 0; j < leak_tracep->index; j++) {
- + fi.next_frame = (FRAME_ADDR)1;
- + fi.pc = leak_tracep->pcs[j];
- + print_frame_info (&fi, j, 0, 0);
- + }
- + }
- + }
- + }
- +
- /* Print briefly all stack frames or just the innermost COUNT frames. */
-
- static void
- ***************
- *** 1222,1227 ****
- --- 1591,1600 ----
- backtrace_limit = 30;
- #endif
-
- + add_com ("leaks_trace", class_stack, leaks_trace_command, "");
- + add_com ("leaks_malloc", class_stack, leaks_malloc_command, "");
- + add_com ("leaks_free", class_stack, leaks_free_command, "");
- + add_com ("leaks_print", class_stack, leaks_print_command, "");
- add_com ("return", class_stack, return_command,
- "Make selected stack frame return to its caller.\n\
- Control remains in the debugger, but when you continue\n\
-
- ================================================================================
- --
- Bertil Askelid
- ebubra@ebu.ericsson.se
-