#!@perl@ ################################################################################ # # Authors : D.Bertini and M.Ivanov # Date: 23/10/2000 # Updated: 10/05/2001 D.Bertini, v2.0 port to gdb5.0 + glibc2.2 # # # To activate the memory checker you have to set in the .rootrc file # the resource Root.MemCheck to 1 (e.g.: Root.MemCheck: 1) and you # have to link with libNew.so (e.g. use root-config --new --libs) or # use rootn.exe. When all this is the case you will find at the end # of the program execution a file "memcheck.out" in the directory # where you started your ROOT program. Alternatively you can set # the resource Root.MemCheckFile to the name of a file to which # the leak information will be written. The contents of this # "memcheck.out" file can be analyzed and transformed into printable # text via the memprobe program (in $ROOTSYS/bin). # ################################################################################ if ( ( $#ARGV < 0 )) { Usage(); } use Getopt::Std; getopt "h,e,m,d,f"; #Usage("exec file is mandatory !\n") if ( !($opt_h or $opt_e) ); if ( $opt_h ) { Usage();} if ( $opt_e ) { $ExeFile=$opt_e; }else { if (!$opt_h){ Usage("an exec file must be provided !\n");} } if ( $opt_m ) { $MemStatFile=$opt_m; }else{ $MemStatFile="memcheck.out"; } if ( $opt_d ) { $MemDescFile=$opt_d; }else{ $MemDescFile="memcheckdesc.out"; } if ( $opt_f ) { $MemFiltFile=$opt_f; }else{ $MemFiltFile="analfilter"; } sub Usage { my ($txt) = @_; print "MemProbe v2.0 Usage:\n"; print "\t $0 -e exec-file [options] [files] \n "; print "\t-e \t\t\tname of analyzed program\n"; print "Options: \n"; print "\t-h \t\t\tthis usage help\n"; print "\t-m \t\tname of analyzed program output file\n"; print "\t-d \t\tname of the stat description file\n"; print "\t-f \t\tname of the filter file\n"; print "\nWarning: $txt\n" if $txt; exit 2; } # old version #$ExeFile = shift @ARGV; #$MemStatFile = $#ARGV >= 0 ? shift @ARGV : "memcheck.out"; open (MEMSTAT, $MemStatFile) or die "-E- Could not open leaks data file $MemStatFile: $!"; if ($#ARGV >= 0) { $BreakOn = shift @ARGV; # Rest in @ARGV are program arguments } ############################################################################## # Read filter expression ############################################################################## open (FILTERFILE, $MemFiltFile); @FILTER = ; close (FILTERFILE); for ($j = 0; $j <= $#FILTER; ++$j){ chop $FILTER[$j]; # print $FILTER[$j]; } ############################################################################## # Find leak adresses ############################################################################## $n = $u = 0; $nfull = $ufull = 0; while () { # remove newline chop $_ ; if (/\s*(stack:)\s*/) { $addrfull = $'; #obtain stack info $_ = $addrfull; s/(st)//g; s/(0x)/:0x/g; @arr = split(/:/); #obtain different leak points for ($i = 1; $i < $#arr; ++$i){ $addr = $arr[$i]; $u++ if not exists $Size{$addr}; $Size{$addr} += 0; $n++; } } } close (MEMSTAT); ############################################################################## # FIND debug info for addresses - information stored # in file leak.desc.C ############################################################################## # redirect standard output to the trash open (STDERR,">/dev/null"); # Redirect list of commands to a file # using unix pipes (needed for RH.7.1 & gdb5.0) open (myFile,">commands"); # Change set listsize 2 to something else to show more lines print myFile "set prompt\nset listsize 1\nset height 0\nset verbose off\n"; if (defined($BreakOn)) { print myFile "break $BreakOn\n"; print myFile "run ", join(" ", @ARGV), " \n"; } else{ print myFile "break main \n "; print myFile "run ", join(" ", @ARGV), " \n"; } foreach (sort keys %Size) { print myFile "l \*$_\n "; } if (defined($BreakOn)) { print myFile "kill\n"; } close (myFile); ############################################################################## # Calling now gdb services in batch mode using # dumped list of commands. # ==> works with gdb-4.18 gdb-5.0 release versions ############################################################################## open (PIPE, "| gdb -n -q $ExeFile < commands > $MemDescFile") or die "-E- Cannot start gdb !!!"; close (PIPE); ############################################################################## # ASIGN debug info to address ############################################################################## open (DBGinfo, $MemDescFile) or die "-E- Could not open desc file $MemDescFile: $!"; $addr =0; while (){ # if (/(0x[0-9a-f]+)\s*/){ if (/(^0x[0-9a-f]+)/){ $addr = $1; #print $addr; /is in /; # print $'; # print "\n"; $filename{$addr} =$'; # print "$addr $filename{$addr}\n"; }else{ $line{$addr} = $_ if ! /^\s*l/; } for ($j = 0; $j <= $#FILTER; ++$j){ if ( ( $line{$addr} =~ /(${FILTER[$j]})/ ) || ( $filename{$addr} =~ /(${FILTER[$j]})/ )){ $line{$addr} = "filtered"; } } } close (DBGinfo); ############################################################################## # FIND unique leak stack sequences ############################################################################## open (MEMSTAT, $MemStatFile) or die "-E- Could not open leaks data file $MemStatFile: $!"; while () { # remove newline chop $_ ; if (/\s*(stack:)\s*/) { $addrfull = $'; #obtain stack info $info =$`; $info =~ s/size //; #obtain mem info for given stack sequence $_ = $addrfull; s/(st)//g; s/(0x)/:0x/g; s/ //g; @arr = split(/:/); #obtain leak points $addrfull ="stack"; $FilterOut=0; for ($i = 1; ($i <= $#arr)&&($FilterOut==0); ++$i){ $addr = $arr[$i]; if ( $line{$addr} =~ /filtered/) { $FilterOut=1; } if ( $filename{$addr} =~ /\)/) { $addrfull .= ":"; $addrfull .= $addr; } } if ($FilterOut==0){ $_= $info; @meminfo = split(/:/); $ufull++ if not exists $Sizefull{$addrfull}; $SizeTotal{$addrfull} += $meminfo[1]; $CountTotal{$addrfull}+= $meminfo[0]; $SizeLeak{$addrfull} += $meminfo[3]; $CountLeak{$addrfull}+= $meminfo[2]; $nfull++; } } } print STDERR "total memory corrupted at point==> ($nfull) \n"; print STDERR "unique allocations ==> ($ufull) \n"; close (MEMSTAT); ############################################################################## # PRINT output leak information to leak.info.C output file ############################################################################## open (LEAKinfo,">leak.info"); open (MULTI,">multidelete.info"); open (MEMSTAT,">memcheck.info"); # sort by size of leak sub bysize { $SizeLeak{$b} <=> $SizeLeak{$a}; # presuming integers } sub bysizetotal { $SizeTotal{$b} <=> $SizeTotal{$a}; # presuming integers } foreach (sort bysizetotal keys %SizeTotal) { print MEMSTAT "Count: $CountTotal{$_}($CountLeak{$_}) Size: $SizeTotal{$_}($SizeLeak{$_}) \n"; s/stack://g; @arr = split(/:/); #obtain leak points for ($i = 0; $i <= $#arr; ++$i){ $addr = $arr[$i]; print MEMSTAT "$filename{$addr}$line{$addr}"; } print MEMSTAT "\n\n"; } foreach (sort bysize keys %SizeLeak) { if ($CountLeak{$_}<0){ print MULTI "Multiple deallocation :$CountLeak{$_}\n"; s/stack://g; @arr = split(/:/); #obtain leak points for ($i = 0; $i <= $#arr; ++$i){ $addr = $arr[$i]; print MULTI "$filename{$addr}$line{$addr}"; } print MULTI "\n\n"; } if ($SizeLeak{$_}!=0){ print LEAKinfo "Leaked allocations: $CountLeak{$_} \t Leaked size: $SizeLeak{$_} \n"; s/stack://g; @arr = split(/:/); #obtain leak points for ($i = 0; $i <= $#arr; ++$i){ $addr = $arr[$i]; print LEAKinfo "$filename{$addr}$line{$addr}"; } print LEAKinfo "\n\n"; } } close(LEAKinfo); close(MULTI); close(MEMSTAT); $remove = 'rm -rf commands'; exec $remove; die " -E- no commands file !";