From 15bf8576988f25b44fdbdf59fc18b1e5576b3277 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 3 Aug 2005 18:01:43 +0000 Subject: [PATCH] Emit output intended for Emacs' compile-mode, instead of for cscope. --- src/utils/checkbochs-trace | 234 +++++++++++++++++++++++++++---------- 1 file changed, 173 insertions(+), 61 deletions(-) diff --git a/src/utils/checkbochs-trace b/src/utils/checkbochs-trace index c7c7f03..fd33d49 100755 --- a/src/utils/checkbochs-trace +++ b/src/utils/checkbochs-trace @@ -3,14 +3,19 @@ # Written by Sorav Bansal . # Modified by Ben Pfaff . +use strict; +use warnings; + +use Getopt::Long qw(:config require_order bundling); +use Cwd qw(realpath); + our $CSCOPE = $ENV{CSCOPE} || "cscope"; our $invoke_cscope = 0; our $nth = 1; our $checkbochs_log = "checkbochs.log"; our $btrace_file = "btrace"; our $kernel_o; - -use Getopt::Long qw(:config require_order bundling); +my $phys_base = "0xc0000000"; sub usage { my ($exitcode) = @_; @@ -19,83 +24,129 @@ checkbochs-trace, to interpret logs generated using 'pintos --checkbochs' Usage: checkbochs [OPTION...] Options: --log=LOG Log file to read is LOG (default: checkbochs.log) - --output=OUT File to write in cscope format is OUT (default: btrace) --object=OBJECT Kernel object file (default: build/kernel.o or kernel.o) - --cscope Invoke cscope on backtraces written to OUT - -n, -nth=N Print a backtrace for the Nth warning (default: 1) + --phys-base=ADDR Value of PHYS_BASE (default: 0xc0000000) + -n, --nth=N Print a backtrace for the Nth warning (default: 1) -h, --help Print this help message EOF exit $exitcode; } GetOptions ("log=s" => \$checkbochs_log, - "output=s" => \$btrace_file, "object=s" => \$kernel_o, "n|nth=i" => \$nth, - "cscope" => sub { $invoke_cscope = 1; }, + "phys-base=s" => \$phys_base, "h|help" => sub { usage (0); } ) or exit 1; +our ($PHYS_BASE) = hex ($phys_base); -open (LOG, "<", $checkbochs_log) or die "$checkbochs_log: open: $!\n"; -our ($done, $loc); -our ($count) = 0; -while () { - if (/Warning on location (.*), backtrace:/) { - $count++; - $loc = $1 if $count == $nth; +# Find kernel.o. +if (!defined $kernel_o) { + if (-e 'kernel.o') { + $kernel_o = 'kernel.o'; + } elsif (-e 'build/kernel.o') { + $kernel_o = 'build/kernel.o'; + } else { + print "can't find kernel.o or build/kernel.o\n"; + return; } +} elsif (! -e $kernel_o) { + print "$kernel_o: stat: $!\n"; + return; } -close LOG; +# Allow Emacs' compile-mode to sync up with our directory. +my ($build_dir) = realpath ($kernel_o); +$build_dir =~ s%/[^/]+$%%; +print "checkbochs-trace: Entering directory `$build_dir'\n\n"; + +# Count race conditions. +my ($count) = read_log (); if ($count == 0) { - print "No potential race conditions detected\n"; + print "No potential race conditions detected.\n"; exit 0; } elsif ($nth > $count) { - print "Only $count potential race conditions detected\n"; + print "Only $count potential race conditions detected.\n"; exit 0; } else { - print "$count potential race conditions detected\n"; + print "$count potential race conditions detected.\n"; } +print "\n"; + +# Get our race condition. +my ($bts, $min_loc, $max_loc) = read_log ($nth); + +# Find out what threads are involved. +our (%threads); +$threads{hex $_} = 1 foreach $bts =~ /thread ([0-9a-f]+)/gi; -my ($name) = lookup_symbol (hex ($loc)); -print "Potential race condition $nth on data at $loc"; +print "Information about potential race #$nth.\n"; +my $name = lookup_symbol (hex ($min_loc)); +my $byte_cnt = hex ($max_loc) - hex ($min_loc) + 1; +print "Potential race condition on data in ", $byte_cnt, " byte"; +print "s" if $byte_cnt != 1; +print " at $min_loc"; print " ($name)" if defined $name; -print "\n"; +print ".\n\n"; + +my (@lockset); +my ($btnum) = 0; +foreach (split ("\n", $bts)) { + my ($s1, $s2, $thread, $eips, $locks) = + /^([A-Z]+)(?:->([A-Z]+))?: thread ([^,]+), backtrace([^,]*), locks([^,]*)/; -print "Writing backtraces to $btrace_file\n"; + my (%state_map) = ('V' => 'Virgin', + 'E' => 'Exclusive', + 'S' => 'Shared', + 'SM' => 'Shared+Modified'); + if (defined $s2) { + print "$state_map{$s1} -> $state_map{$s2} "; + } else { + print "Reduced lockset in $state_map{$s1} "; + } + print "in thread $thread.\n"; -$btnum = 0; -open (LOG, "<", $checkbochs_log) or die "$checkbochs_log: open: $!\n"; -open (OUT, ">", $btrace_file) or die "$btrace_file: create: $!\n"; -while () { - if (my ($op, $bt) = /^(.*) location $loc.*backtrace: (.*)$/) { - if ($op =~ /Thread (\S+): Warning on/) { - $op = "Thread $1: Potential race"; + my (@locks) = sort {$a <=> $b} map (hex ($_), split (' ', $locks)); + print_lockset ("Locks held", "none", @locks); + + if ($s1 ne 'V') { + if ($btnum++ == 0) { + @lockset = @locks; + print_lockset ("Lockset", "empty", @lockset); } else { - $op = "Thread $op"; + my (@old_lockset) = @lockset; + @lockset = intersect (\@lockset, \@locks); + if (@old_lockset != @lockset || @lockset == 0) { + print_lockset ("Lockset", "empty", @lockset); + } else { + print "Lockset unchanged.\n"; + } } - open (BT, "backtrace kernel.o $bt |" ); - while () { - chomp; - my ($addr, $func, $fn, $ln) - = /^([a-f0-9]+): (\S+) \(([^:]+):([^\)]+)\)/ - or next; - $fn =~ s%^(\.\./)+%%; - print OUT "$fn $func $ln $op\n"; - $op = '.'; + } + + my ($btline) = 0; + open (BT, "backtrace kernel.o $eips |" ); + while () { + chomp; + my ($addr, $func, $fn, $ln) + = /^([a-f0-9]+): (\S+) \(([^:]+):([^\)]+)\)/ + or next; + print "$fn:$ln: "; + if ($btline++ == 0) { + print "occurred here in $func() at 0x$addr...\n"; + } else { + print "...called from $func() at 0x$addr\n"; } - close BT; - $btnum++; } + close BT; + print "\n"; } -close OUT; -close LOG; -exec ("$CSCOPE -R -F$btrace_file") if $invoke_cscope; +print "checkbochs-trace: Leaving directory `$build_dir'\n"; sub lookup_symbol { - my ($address) = 0; + my ($address) = @_; # Find nm. my ($nm) = search_path ("i386-elf-nm") || search_path ("nm"); @@ -104,21 +155,6 @@ sub lookup_symbol { return; } - # Find kernel.o. - if (!defined $kernel_o) { - if (-e 'kernel.o') { - $kernel_o = 'kernel.o'; - } elsif (-e 'build/kernel.o') { - $kernel_o = 'build/kernel.o'; - } else { - print "can't find kernel.o or build/kernel.o\n"; - return; - } - } elsif (! -e $kernel_o) { - print "$kernel_o: stat: $!\n"; - return; - } - # Use nm to find name. if (!open (NM, "$nm -S $kernel_o|")) { print "nm: exec: $!\n"; @@ -126,7 +162,7 @@ sub lookup_symbol { } while () { my ($h_start, $h_length, $type, $name) - = /^([a-f0-9]+) ([a-f0-9]+) \S (\S+)$/ or next; + = /^([a-f0-9]+) ([a-f0-9]+) (\S) (\S+)$/ or next; my ($start) = hex ($h_start); my ($length) = hex ($h_length); next if $address < $start || $address >= $start + $length; @@ -135,6 +171,18 @@ sub lookup_symbol { return $name; } close (NM); + + # Check whether the address is in a thread struct. + if ($address >= $PHYS_BASE && $address < $PHYS_BASE + 64 * 1024 * 1024) { + my ($page) = ($address - $PHYS_BASE) >> 12; + if (defined $threads{$page}) { + my ($pg_ofs) = $address & 0xfff; + return sprintf ("in thread %x %s", $page, + $pg_ofs >= 0x800 ? "stack" : "data"); + } + } + + return; } sub search_path { @@ -145,3 +193,67 @@ sub search_path { } return undef; } + +sub read_log { + my ($nth) = @_; + + open (LOG, "<", $checkbochs_log) or die "$checkbochs_log: open: $!\n"; + my ($min_loc, $max_loc); + my ($count) = 0; + my ($bts) = ''; + my ($last_bts) = ''; + while () { + if (my ($loc) = /^Warning on location (.*):$/) { + $bts = ''; + while () { + last if /^$/; + $bts .= $_; + } + if ($bts ne $last_bts || hex ($loc) != hex ($min_loc) + 1) { + last if defined ($nth) && $nth == $count; + $min_loc = $max_loc = $loc; + $last_bts = $bts; + $count++; + } else { + $max_loc = $loc; + } + } + } + close LOG; + + return ($last_bts, $min_loc, $max_loc) if defined ($nth) && $nth == $count; + return ($count); +} + +sub intersect { + my ($set1, $set2) = @_; + my (%set); + $set{$_}++ foreach @$set1; + $set{$_}++ foreach @$set2; + my (@out); + $set{$_} == 2 && push (@out, $_) foreach keys %set; + return sort {$a <=> $b} @out; +} + +sub print_lockset { + my ($name, $empty, @locks) = @_; + print "$name:"; + if (@locks) { + my ($if) = 0; + my ($lock_pfx) = 0; + foreach my $lock (@locks) { + if ($lock == 0xffff0001) { + $if = 1; + } elsif ($lock == 0xffff0002) { + $lock_pfx = 1; + } else { + printf " %x", $lock; + } + } + print " (interrupts disabled)" if $if; + print " (LOCK prefix)" if $lock_pfx; + print ".\n"; + } else { + print " $empty.\n"; + } +} -- 2.30.2