#! @PERL@ eval "exec @PERL@ -S $0 $@" if 0; # Copyright (C) 1997-2023 Free Software Foundation, Inc. # This file is part of the GNU C Library. # Based on the mtrace.awk script. # The GNU C Library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The GNU C Library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with the GNU C Library; if not, see # . $VERSION = "@VERSION@"; $PKGVERSION = "@PKGVERSION@"; $REPORT_BUGS_TO = '@REPORT_BUGS_TO@'; $progname = $0; sub usage { print "Usage: mtrace [OPTION]... [Binary] MtraceData\n"; print " --help print this help, then exit\n"; print " --version print version number, then exit\n"; print "\n"; print "For bug reporting instructions, please see:\n"; print "$REPORT_BUGS_TO.\n"; exit 0; } # We expect two arguments: # #1: the complete path to the binary # #2: the mtrace data filename # The usual options are also recognized. arglist: while (@ARGV) { if ($ARGV[0] eq "--v" || $ARGV[0] eq "--ve" || $ARGV[0] eq "--ver" || $ARGV[0] eq "--vers" || $ARGV[0] eq "--versi" || $ARGV[0] eq "--versio" || $ARGV[0] eq "--version") { print "mtrace $PKGVERSION$VERSION\n"; print "Copyright (C) 2022 Free Software Foundation, Inc.\n"; print "This is free software; see the source for copying conditions. There is NO\n"; print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"; print "Written by Ulrich Drepper \n"; exit 0; } elsif ($ARGV[0] eq "--h" || $ARGV[0] eq "--he" || $ARGV[0] eq "--hel" || $ARGV[0] eq "--help") { &usage; } elsif ($ARGV[0] =~ /^-/) { print "$progname: unrecognized option `$ARGV[0]'\n"; print "Try `$progname --help' for more information.\n"; exit 1; } else { last arglist; } } if ($#ARGV == 0) { $binary=""; $data=$ARGV[0]; } elsif ($#ARGV == 1) { $binary=$ARGV[0]; $data=$ARGV[1]; if ($binary =~ /^.*[\/].*$/) { $prog = $binary; } else { $prog = "./$binary"; } # Set the environment variable LD_TRACE_LOADED_OBJECTS to 2 so the # executable is also printed. if (open (locs, "env LD_TRACE_LOADED_OBJECTS=2 $prog |")) { while () { chop; if (/^.*=> (.*) .(0x[0123456789abcdef]*).$/) { $locs{$1} = $2; $rel{$1} = hex($2); } } close (LOCS); } } else { die "Wrong number of arguments, run $progname --help for help."; } sub addr2line { my $addr = pop(@_); my $prog = pop(@_); if (open (ADDR, "addr2line -e $prog $addr|")) { my $line = ; chomp $line; close (ADDR); if ($line ne '??:0') { return $line } } } sub location { my $str = pop(@_); return $str if ($str eq ""); if ($str =~ /.*[[](0x[^]]*)]:(.)*/) { my $addr = $1; my $fct = $2; return $cache{$addr} if (exists $cache{$addr}); if ($binary ne "") { my $line = &addr2line($binary, $addr); if ($line) { $cache{$addr} = $line; return $cache{$addr}; } } $cache{$addr} = $str = "$fct @ $addr"; } elsif ($str =~ /^(.*):.*[[](0x[^]]*)]$/) { my $prog = $1; my $addr = $2; my $searchaddr; return $cache{$addr} if (exists $cache{$addr}); $searchaddr = sprintf "%#x", hex($addr) + $rel{$prog}; if ($binary ne "") { for my $address ($searchaddr, $addr) { my $line = &addr2line($prog, $address); if ($line) { $cache{$addr} = $line; return $cache{$addr}; } } } $cache{$addr} = $str = $addr; } elsif ($str =~ /^.*[[](0x[^]]*)]$/) { my $addr = $1; return $cache{$addr} if (exists $cache{$addr}); if ($binary ne "") { my $line = &addr2line($binary, $addr); if ($line) { $cache{$addr} = $line; return $cache{$addr}; } } $cache{$addr} = $str = $addr; } return $str; } $nr=0; open(DATA, "<$data") || die "Cannot open mtrace data file"; while () { my @cols = split (' '); my $n, $where; if ($cols[0] eq "@") { # We have address and/or function name. $where=$cols[1]; $n=2; } else { $where=""; $n=0; } $allocaddr=$cols[$n + 1]; $howmuch=hex($cols[$n + 2]); ++$nr; SWITCH: { if ($cols[$n] eq "+") { if (defined $allocated{$allocaddr}) { printf ("+ %#0@XXX@x Alloc %d duplicate: %s %s\n", hex($allocaddr), $nr, &location($addrwas{$allocaddr}), $where); } elsif ($allocaddr =~ /^0x/) { $allocated{$allocaddr}=$howmuch; $addrwas{$allocaddr}=$where; } last SWITCH; } if ($cols[$n] eq "-") { if (defined $allocated{$allocaddr}) { undef $allocated{$allocaddr}; undef $addrwas{$allocaddr}; } else { printf ("- %#0@XXX@x Free %d was never alloc'd %s\n", hex($allocaddr), $nr, &location($where)); } last SWITCH; } if ($cols[$n] eq "<") { if (defined $allocated{$allocaddr}) { undef $allocated{$allocaddr}; undef $addrwas{$allocaddr}; } else { printf ("- %#0@XXX@x Realloc %d was never alloc'd %s\n", hex($allocaddr), $nr, &location($where)); } last SWITCH; } if ($cols[$n] eq ">") { if (defined $allocated{$allocaddr}) { printf ("+ %#0@XXX@x Realloc %d duplicate: %#010x %s %s\n", hex($allocaddr), $nr, $allocated{$allocaddr}, &location($addrwas{$allocaddr}), &location($where)); } else { $allocated{$allocaddr}=$howmuch; $addrwas{$allocaddr}=$where; } last SWITCH; } if ($cols[$n] eq "=") { # Ignore "= Start". last SWITCH; } if ($cols[$n] eq "!") { # Ignore failed realloc for now. last SWITCH; } } } close (DATA); # Now print all remaining entries. @addrs= keys %allocated; $anything=0; if ($#addrs >= 0) { foreach $addr (sort @addrs) { if (defined $allocated{$addr}) { if ($anything == 0) { print "\nMemory not freed:\n-----------------\n"; print ' ' x (@XXX@ - 7), "Address Size Caller\n"; $anything=1; } printf ("%#0@XXX@x %#8x at %s\n", hex($addr), $allocated{$addr}, &location($addrwas{$addr})); } } } print "No memory leaks.\n" if ($anything == 0); exit $anything != 0;