3 # Worldvisions Weaver Software:
4 # Copyright (C) 2005 Net Integration Technologies, Inc.
6 # Post-process WvCrash output using GDB, to try to figure out exactly
7 # which line things died upon.
9 # The advantage of using this programme over a plain stack dump is
10 # that it is clever enough to figure out relative offsets to a
11 # function, so you don't need the exact same object file as the one
12 # that crashed. With extra cleverness, we even span symbol manging
13 # across different versions of G++.
24 my $DEBUG = $ENV{DEBUG
};
27 print(STDERR
@_) if $DEBUG;
32 my $target = shift(@_);
34 return $target if (-f
$target && -x
$target);
37 my $endtime = time() + 20; # Stop searching after 20 seconds.
38 my $cwd = cwd
(); # Save the working dir so we can go back.
41 if ($_ eq $target && -f
$_ && -x
$_) {
42 $result = $File::Find
::name
;
43 die; # Break out of the File::Find::find.
45 elsif (time() > $endtime){
50 # Traverse desired filesystems
52 File
::Find
::find
({wanted
=> $wanted,
54 follow_skip
=> 2}, '.');
64 my $gdb_prompt = qr/(?:\(gdb\) )+/;
65 my $gdb_flush = "echo \\n\n"; # Flush gdb's stdout
67 my $gdb_addr = sub ($$$) {
68 my ($Reader, $Writer, $function) = @_;
70 debug
("-> info addr $function\n");
71 print($Writer "info addr $function\n");
72 print($Writer $gdb_flush);
77 if (/^$gdb_prompt\s*Symbol "(.*?)" is .* (0x[0-9A-Fa-f]+)/) {
80 last if (/^$gdb_prompt$/ && $first);
84 # Returns the human-readable function name, and the starting address
85 return ($function, undef);
88 my $gdb_line = sub ($$$$)
90 my ($Reader, $Writer, $addr, $offset) = @_;
92 debug
("-> info line *$addr+$offset\n");
93 print($Writer "info line *$addr+$offset\n");
94 print($Writer $gdb_flush);
98 if (/^$gdb_prompt\s*Line ([0-9]+) of "(.*?)" starts at /) {
101 last if (/^$gdb_prompt$/ && $first);
105 # Returns the filename and line number.
106 return (undef, undef);
110 my ($Reader, $Writer);
111 debug
("gdb '$programme'\n");
112 my $pid = open2
($Reader, $Writer, "gdb '$programme' 2>&1") or die;
113 print($Writer "set width 2000\n");
114 print($Writer "set height 20000\n");
115 print($Writer "break main\n");
116 print($Writer "run\n");
117 print($Writer $gdb_flush);
120 last if (/^$gdb_prompt$/);
126 # Parse the input string.
127 my ($binary, $absolute) = ($string =~ /^(.*?)([\[\(].*)/);
128 my ($function, $offset) = ($absolute =~ /^\((.*?)\+(.*?)\)/);
129 $absolute =~ s/.*\[(.*?)\].*/$1/;
131 my ($file, $line) = ("--", "--");
133 unless (defined($function)) {
134 $function = "file: $binary";
139 # Try with the mangled function name
140 ($function, $addr) = &$gdb_addr($Reader, $Writer, $function);
142 unless (defined($addr))
144 # Try with c++filt mangled function
145 my $filtered = `c++filt '$function'` or return ($function,
148 # Turn () into (void) for old GDB
149 $filtered =~ s/(?<!operator)\(\)/(void)/g;
150 ($function, $addr) = &$gdb_addr($Reader, $Writer, $filtered);
152 unless (defined($addr))
154 # Try turning (void) into () for new GDB
155 $function =~ s/\(void\)/()/g;
156 ($function, $addr) = &$gdb_addr($Reader, $Writer,
160 return ($function, undef, undef) unless (defined($addr));
163 ($file, $line) = &$gdb_line($Reader, $Writer, $addr, $offset);
164 $file = "--" unless defined($file);
165 $line = "--" unless defined($line);
168 return ($function, $file, $line);
173 my $programme; # Base name of the programme
174 my $programme_path; # Relative path to the location of programme
175 my $signum; # Signal number that it died on
176 my $gdb_parse; # Handle to GDB conversation
180 while (my $string = <>)
184 # We drive a state machine here, to figure out what we should do next.
191 # We will want to skip anything that isn't the beginning
193 if ($string =~ / dying on signal \d+/)
201 # Extract the information out of the first line of the header.
202 if ($string =~ /^(.*?) dying on signal (\d+)(.*)?/)
206 my $signame = $3 || "";
208 if ($programme =~ s/\s+\((.*)\)//) {
209 $version = " ($1)" if $1;
211 $programme = basename
($programme); # Relative path
212 print("$programme$version dying on signal $signum$signame\n");
213 if ($programme_path = find_programme
($programme)) {
223 # We don't actually have much of a header right now, so this
224 # merely transitions to the backtrace.
225 if ($string =~ /^Backtrace:/)
227 unless (defined($programme) && defined($signum)) {
232 $gdb_parse = gdb_init
($programme_path);
233 $state = "backtrace";
236 # Echo back header information. It's not important to parse,
237 # but it might be nice to display.
242 # Keep reading backtrace information until we stop seeing
244 if ($string =~ /\[0x[0-9A-Fa-f]+\]$/)
246 my ($function, $file, $line) = &$gdb_parse($string);
248 # Eat up some extra spaces
249 $function =~ s/,\s+/,/g;
250 $function =~ s/\s+\*/*/g;
251 $function =~ s/\s+&/&/g;
252 $function =~ s/\s+&/&/g;
253 $function =~ s/\s+\(/\(/g;
255 $state = "backtrace";
256 if (not defined($file)) {
257 printf("%-40s --:--\n", $function);
260 printf("%-40s %s:%s\n", $function, $file, $line);
272 "Could not find the '$programme' program! ",
278 print(STDERR
"Unrecognized WvCrash output. Aborting.\n");
283 die("Internal wvcrashread error. Aborting.");