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\

Newsgroups: gnu.gdb.bug