| #!/usr/bin/perl -w | |
| # ----------------------------------------------------------------------------- | |
| # | |
| # Relay-checker. | |
| # | |
| # This program will inspect a log file with relay information and tell you | |
| # whether calls and returns match. If not, this suggests that the parameter | |
| # list might be incorrect. (It could be something else also.) | |
| # | |
| # This program now accepts a second command line parameter, which will enable | |
| # a "full" listing format; otherwise a trimmed down simplified listing is | |
| # generated. It does not matter what the second command line parameter is; | |
| # anything will enable the full listing. | |
| # | |
| # Copyright 1997-1998 Morten Welinder (terra@diku.dk) | |
| # 2001 Eric Pouech | |
| # | |
| # This 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. | |
| # | |
| # This 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 this library; if not, write to the Free Software | |
| # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| # ----------------------------------------------------------------------------- | |
| use strict; | |
| my $srcfile = $ARGV[0]; | |
| my $fullformat = $ARGV[1]; | |
| my %tid_callstack = (); | |
| my $newlineerror = 0; | |
| my $indentp = 1; | |
| my $lasttid = 0; | |
| open (IN, "<$srcfile") || die "Cannot open $srcfile for reading: $!\n"; | |
| LINE: | |
| while (<IN>) { | |
| if (/^([0-9a-f]+):Call ([A-Za-z0-9]+\.[A-Za-z0-9_]+)\((.*\)) .*/) { | |
| my $tid = $1; | |
| my $func = $2; | |
| if (defined $fullformat) { | |
| if ($lasttid ne $tid) { | |
| print "******** thread change\n" | |
| } | |
| $lasttid = $tid; | |
| print ($indentp ? (' ' x 2 x (1 + $#{$tid_callstack{$tid}})) : ''); | |
| print "$_"; | |
| } | |
| # print "have call func=$func $_"; | |
| if (/ ret=(........)$/ || | |
| / ret=(....:....) (ds=....)$/ || | |
| / ret=(........) fs=....$/) { | |
| my $retaddr = $1; | |
| my $segreg = $2; | |
| $segreg = "none" unless defined $segreg; | |
| push @{$tid_callstack{$tid}}, [$func, $retaddr, $segreg]; | |
| next; | |
| } elsif (not eof IN) { | |
| # Assume a line got cut by a line feed in a string. | |
| $_ .= scalar (<IN>); | |
| if (!$newlineerror) { | |
| print "Err[$tid] string probably cut by newline at line $. .\n"; | |
| $newlineerror = 1; | |
| } | |
| # print "[$_]"; | |
| redo; | |
| } | |
| } | |
| elsif (/^([0-9a-f]+):Call (window proc) ([0-9a-fx]+) .*/) { | |
| my $tid = $1; | |
| my $func = $2; | |
| my $retaddr = $3; | |
| my $segreg = "none"; | |
| if (defined $fullformat) { | |
| if ($lasttid ne $tid) { | |
| print "******** thread change\n" | |
| } | |
| $lasttid = $tid; | |
| print ($indentp ? (' ' x 2 x (1 + $#{$tid_callstack{$tid}})) : ''); | |
| print "$_"; | |
| } | |
| push @{$tid_callstack{$tid}}, [$func, $retaddr, $segreg]; | |
| } | |
| elsif (/^([0-9a-f]+):Ret ([A-Za-z0-9]+\.[A-Za-z0-9_]+)\(.*\) .* ret=(........)$/ || | |
| /^([0-9a-f]+):Ret ([A-Za-z0-9]+\.[A-Za-z0-9_]+)\(.*\) .* ret=(....:....) (ds=....)$/ || | |
| /^([0-9a-f]+):Ret ([A-Za-z0-9]+\.[A-Za-z0-9_]+)\(.*\) .* ret=(........) fs=....$/ || | |
| /^([0-9a-f]+):RET ([A-Za-z0-9]+\.[A-Za-z0-9_]+: [A-Za-z0-9]+)\(.*\) .* ret=(........)$/ || | |
| /^([0-9a-f]+):Ret (window proc) ([0-9a-fx]+) .*/) { | |
| my $tid = $1; | |
| my $func = $2; | |
| my $retaddr = $3; | |
| my $segreg = $4; | |
| my ($topfunc,$topaddr,$topseg); | |
| if (defined $fullformat) { | |
| if ($lasttid ne $tid) { | |
| print "******** thread change\n" | |
| } | |
| $lasttid = $tid; | |
| } | |
| # print "have ret func=$func <$_>\n"; | |
| if (!defined($tid_callstack{$tid})) | |
| { | |
| print "Err[$tid]: unknown tid\n"; | |
| next; | |
| } | |
| $segreg = "none" unless defined $segreg; | |
| POP: | |
| while (1) { | |
| if ($#{$tid_callstack{$tid}} == -1) { | |
| print "Err[$tid]: Return from $func to $retaddr with empty stack.\n"; | |
| next LINE; | |
| } | |
| ($topfunc,$topaddr,$topseg) = @{pop @{$tid_callstack{$tid}}}; | |
| if ($topfunc ne $func) { | |
| print "Err[$tid]: Return from $topfunc, but call from $func.\n"; | |
| next POP; | |
| } | |
| last POP; | |
| } | |
| my $addrok = ($topaddr eq $retaddr); | |
| my $segok = ($topseg eq $segreg); | |
| if ($addrok && $segok) { | |
| if (defined $fullformat) { | |
| print ($indentp ? (' ' x 2 x (1 + $#{$tid_callstack{$tid}})) : ''); | |
| print "$_"; | |
| } else { | |
| print "Ok [$tid]: ", ($indentp ? (' ' x (1 + $#{$tid_callstack{$tid}})) : ''); | |
| print "$func from $retaddr with $segreg.\n"; | |
| } | |
| } else { | |
| print "Err[$tid]: Return from $func is to $retaddr, not $topaddr.\n" | |
| if !$addrok; | |
| print "Err[$tid]: Return from $func with segreg $segreg, not $topseg.\n" | |
| if !$segok; | |
| } | |
| } | |
| else { | |
| print "$_"; | |
| } | |
| } | |
| foreach my $tid (keys %tid_callstack) { | |
| while ($#{$tid_callstack{$tid}} != -1) { | |
| my ($topfunc,$topaddr,$topseg) = @{pop @{$tid_callstack{$tid}}}; | |
| print "Err[$tid]: leftover call to $topfunc from $topaddr.\n"; | |
| } | |
| } | |
| close (IN); |