3 # decode-panic - decode a Mac OS panic log to show source line numbers
4 # see the end of the file for full documentation and license.
7 use English qw( -no_match_vars ) ;
9 use File::Temp qw( tempdir );
17 my $panic_file = "/Library/Logs/panic.log";
20 my $kextload = "/sbin/kextload";
21 my $kextutil = "/usr/bin/kextutil";
23 my $kernel = "/mach_kernel";
24 my $gdb = "/usr/bin/gdb";
27 my $gdb_file = "gdb.input";
28 my $temp_dir = tempdir( "afsdebugXXXXXX", DIR => File::Spec->tmpdir,
29 TMPDIR => 1, CLEANUP => 1 );
30 my $dump_file = "/var/db/openafs/logs/crash.dump";
34 my $result = GetOptions ("input=s" => \$panic_file,
35 "output=s" => \$dump_file,
36 "kernel=s" => \$kernel,
37 "quiet" => \$option_quiet,
38 "help" => \$option_help
42 pod2usage(-message => "Syntax error.",
51 pod2usage(-message => "",
58 # check for necessary programs & panic file
59 for my $program ( $gdb, $kextload ) {
60 if ( ! -x $program ) {
61 if ( $option_quiet ) {
64 croak "Can't find $program!\n"
70 $kextprog = $kextutil;
72 $kextprog = $kextload;
75 croak "Can't find panic file: $panic_file!\n" if ( ! -r $panic_file );
77 read_panic( $panic_file, \%crash_info );
79 if ($crash_info{"kernel_version"} =~ /X86_64/ ) {
83 if ($crash_info{"kernel_version"} =~ /I386/ ) {
87 if ($crash_info{"kernel_version"} =~ /PPC/ ) {
94 generate_symbol_files( $crash_info{"afs_kernel_address"}, $temp_dir, $kextarch );
96 write_gdb_input_file( $temp_dir, $gdb_file, $crash_info{ "backtrace" } );
98 my $gdb_output = `$gdb $gdbarch $kernel -batch -x $temp_dir/$gdb_file`;
99 croak "gdb failed!\n" if $CHILD_ERROR;
101 write_dump_file( $dump_file, \%crash_info, $gdb_output );
103 # read the panic file and parse out the addresses
106 my $filename = shift;
107 my $hash_ref = shift;
111 my @panic_section_positions = ( 0 );
114 my $panic_fh = IO::File->new( $filename, '<' )
115 or croak "Can't open backtrace file $filename: $OS_ERROR\n";
117 # find the last panic section as denoted by "*********"
118 while ( $line = <$panic_fh> ) {
120 if ( $line eq "*********" ) {
123 push @panic_section_positions, $panic_fh->tell;
127 # ignore the empty last section
128 if ( @panic_section_positions > 2 ) {
129 pop @panic_section_positions
132 # Seek to last full panic section
133 # or the beginning of the file if appropriate
134 $panic_fh->seek( $panic_section_positions[-1], 0 );
136 $hash_ref->{ "date" } = <$panic_fh>;
137 chomp $hash_ref->{ "date" };
139 while ( $line = <$panic_fh> ) {
142 #skip lines until "Backtrace" is seen
143 $line =~ /^\s*(Backtrace,|Backtrace:|Backtrace \()/;
149 if ( $option_quiet ) {
152 croak "Couldn't find a backtrace in $filename\n";
156 # gather the backtrace addresses
157 if ( $backtrace eq "Backtrace:" ) {
159 while ( $line = <$panic_fh> ) {
161 last if $line !~ /^\s*(0x[0-9a-fA-F]+)/;
162 my @memory_addresses = split /\s+/, $line;
164 # add non-empty array elements to the list
165 push @{ $hash_ref->{ "backtrace" } },
166 grep { /0x/ } @memory_addresses;
170 while ( $line = <$panic_fh> ) {
172 last if $line !~ /^\s*0x[0-9a-fA-F]+ : (0x[0-9a-fA-F]*)/;
173 push @{ $hash_ref->{ "backtrace" } }, $1;
177 # now we need the address for the afs kernel module
178 while ( $line = <$panic_fh> ) {
180 next if ( $line !~ /org\.openafs\.filesystems\.afs/ );
182 $kernel_line = $line;
183 $line =~ /\@(0x[0-9a-fA-F]+)/;
184 $hash_ref->{ "afs_kernel_address" } = $1;
185 $kernel_line =~ /^\s+([^@]+)@.+/;
186 $hash_ref->{ "afs_info" } = $1;
191 # grab the kernel version
192 while ( $line = <$panic_fh> ) {
194 next if ( $line !~ /^Darwin Kernel Version/ );
195 $hash_ref->{ "kernel_version" } = $line;
199 or croak "Can't close file $filename: $OS_ERROR\n";
201 if ( !$kernel_line ) {
202 if ( $option_quiet ) {
205 croak "No OpenAFS reference found in latest panic!";
210 # generate the symbol files that will be read by gdb
211 sub generate_symbol_files {
212 my $kernel_address = shift;
213 my $symbol_write_dir = shift;
214 my $kextarch = shift;
220 "-a", 'org.openafs.filesystems.afs@' . $kernel_address,
221 "-n", "/Library/OpenAFS/Tools/root.client/usr/vice/etc/afs.kext/" );
223 if ( $CHILD_ERROR ) {
225 croak "kextload failed to run: $OS_ERROR\n";
230 sub write_gdb_input_file {
232 my $write_dir = shift;
233 my $filename = shift;
234 my $backtrace_ref = shift;
236 my @symbol_files = ( $write_dir . "/org.openafs.filesystems.afs.sym" );
238 my $fh = IO::File->new( $write_dir . "/" . $filename, '>' )
239 or croak "Can't open gdb file $filename for writing: $OS_ERROR\n";
241 for my $symbol ( @symbol_files ) {
242 print $fh "add-symbol-file $symbol\n";
245 print $fh "set print asm-demangle on\n";
247 for my $address ( @{ $backtrace_ref } ) {
248 print $fh "x/i $address\n";
252 or croak "Can't close file $filename: $OS_ERROR\n";
255 # write out the pertinent details to a file.
256 sub write_dump_file {
257 my $filename = shift;
258 my $hash_ref = shift;
261 my $log_dir = dirname $filename;
263 if ( ! -d $log_dir ) {
264 mkdir $log_dir, 0755;
265 croak "Can't create directory $log_dir: $OS_ERROR\n" if $CHILD_ERROR;
268 croak "Can't write to folder $log_dir." if ( ! -w $log_dir );
270 my $fh = IO::File->new( $filename, '>', 0664 )
271 or croak "Can't open dump file $filename for writing: $OS_ERROR\n";
273 print $fh "Panic Date: ", $hash_ref->{ "date" }, "\n";
274 print $fh "Kernel Version: ", $hash_ref->{ "kernel_version" }, "\n";
275 print $fh "OpenAFS Version: ", $hash_ref->{ "afs_info" }, "\n";
276 print $fh "=============\n";
280 or croak "Can't close file $filename: $OS_ERROR\n";
287 decode-panic - decode a Mac OS panic log to show source line numbers
291 This documentation refers to decode-panic version $Revision$
295 decode-panic [-i <input panic log>] [-o <output dump file>] [-k <kernel file>] [-q]
299 -i The path to the panic log that should be read
300 -o The path to where the decoded panic log should be written
301 -k The path to the kernel image corresponding to the panic
302 -q Quiet mode - don't complain if there is a problem.
307 It parses the panic log for Mac OS X kernel panics that are caused by
308 openafs in order to produce a human-readable backtrace.
310 This program uses crash isolation procedure as outlined in
311 http://developer.apple.com/technotes/tn2002/tn2063.html#IsolatingCrash
313 Here is an example file that is fed to gdb:
315 add-symbol-file /tmp/afsdebugt8dGOb/org.openafs.filesystems.afs.sym
316 set print asm-demangle on
320 Panic logs can be found in /Library/Logs/panic.log in 10.4 (Tiger),
321 /Library/Logs/PanicReporter/YYYY-MM-DD-HHMMSS.panic in 10.5 (Leopard),
322 and /Library/Logs/DiagnosticReports/Kernel_YYYY-MM-DD-HHMMSS.panic in 10.6
327 This program needs gdb and kextload; Starting in SnowLeopard, it needs kextutil.
329 =head1 BUGS AND LIMITATIONS
331 decode-panic clobbers the output file.
335 Copyright 2008. Jason Edgecombe <jason@rampaginggeek.com>
337 This documentation is covered by the BSD License as written in the
338 doc/LICENSE file in the OpenAFS source tree. This program was written by
339 Jason Edgecombe for OpenAFS.