3 # Written by Sorav Bansal <sbansal@stanford.edu>.
4 # Modified by Ben Pfaff <blp@cs.stanford.edu>.
9 use Getopt::Long qw(:config require_order bundling);
12 our $CSCOPE = $ENV{CSCOPE} || "cscope";
13 our $invoke_cscope = 0;
15 our $checkbochs_log = "checkbochs.log";
16 our $btrace_file = "btrace";
18 my $phys_base = "0xc0000000";
23 checkbochs-trace, to interpret logs generated using 'pintos --checkbochs'
24 Usage: checkbochs [OPTION...]
26 --log=LOG Log file to read is LOG (default: checkbochs.log)
27 --object=OBJECT Kernel object file (default: build/kernel.o or kernel.o)
28 --phys-base=ADDR Value of PHYS_BASE (default: 0xc0000000)
29 -n, --nth=N Print a backtrace for the Nth warning (default: 1)
30 -h, --help Print this help message
35 GetOptions ("log=s" => \$checkbochs_log,
36 "object=s" => \$kernel_o,
38 "phys-base=s" => \$phys_base,
39 "h|help" => sub { usage (0); }
42 our ($PHYS_BASE) = hex ($phys_base);
45 if (!defined $kernel_o) {
47 $kernel_o = 'kernel.o';
48 } elsif (-e 'build/kernel.o') {
49 $kernel_o = 'build/kernel.o';
51 print "can't find kernel.o or build/kernel.o\n";
54 } elsif (! -e $kernel_o) {
55 print "$kernel_o: stat: $!\n";
59 # Allow Emacs' compile-mode to sync up with our directory.
60 my ($build_dir) = realpath ($kernel_o);
61 $build_dir =~ s%/[^/]+$%%;
62 print "checkbochs-trace: Entering directory `$build_dir'\n\n";
64 # Count race conditions.
65 my ($count) = read_log ();
67 print "No potential race conditions detected.\n";
69 } elsif ($nth > $count) {
70 print "Only $count potential race conditions detected.\n";
73 print "$count potential race conditions detected.\n";
77 # Get our race condition.
78 my ($bts, $min_loc, $max_loc) = read_log ($nth);
80 # Find out what threads are involved.
82 $threads{hex $_} = 1 foreach $bts =~ /thread ([0-9a-f]+)/gi;
84 print "Information about potential race #$nth.\n";
85 my $name = lookup_symbol (hex ($min_loc));
86 my $byte_cnt = hex ($max_loc) - hex ($min_loc) + 1;
87 print "Potential race condition on data in ", $byte_cnt, " byte";
88 print "s" if $byte_cnt != 1;
90 print " ($name)" if defined $name;
95 foreach (split ("\n", $bts)) {
96 my ($s1, $s2, $thread, $eips, $locks) =
97 /^([A-Z]+)(?:->([A-Z]+))?: thread ([^,]+), backtrace([^,]*), locks([^,]*)/;
99 my (%state_map) = ('V' => 'Virgin',
102 'SM' => 'Shared+Modified');
104 print "$state_map{$s1} -> $state_map{$s2} ";
106 print "Reduced lockset in $state_map{$s1} ";
108 print "in thread $thread.\n";
110 my (@locks) = sort {$a <=> $b} map (hex ($_), split (' ', $locks));
111 print_lockset ("Locks held", "none", @locks);
116 print_lockset ("Lockset", "empty", @lockset);
118 my (@old_lockset) = @lockset;
119 @lockset = intersect (\@lockset, \@locks);
120 if (@old_lockset != @lockset || @lockset == 0) {
121 print_lockset ("Lockset", "empty", @lockset);
123 print "Lockset unchanged.\n";
129 open (BT, "backtrace kernel.o $eips |" );
132 my ($addr, $func, $fn, $ln)
133 = /^([a-f0-9]+): (\S+) \(([^:]+):([^\)]+)\)/
136 if ($btline++ == 0) {
137 print "occurred here in $func() at 0x$addr...\n";
139 print "...called from $func() at 0x$addr\n";
146 print "checkbochs-trace: Leaving directory `$build_dir'\n";
152 my ($nm) = search_path ("i386-elf-nm") || search_path ("nm");
154 print "neither `i386-elf-nm' nor `nm' in PATH\n";
158 # Use nm to find name.
159 if (!open (NM, "$nm -S $kernel_o|")) {
160 print "nm: exec: $!\n";
164 my ($h_start, $h_length, $type, $name)
165 = /^([a-f0-9]+) ([a-f0-9]+) (\S) (\S+)$/ or next;
166 my ($start) = hex ($h_start);
167 my ($length) = hex ($h_length);
168 next if $address < $start || $address >= $start + $length;
170 $name .= "+" . ($address - $start) if $address != $start;
175 # Check whether the address is in a thread struct.
176 if ($address >= $PHYS_BASE && $address < $PHYS_BASE + 64 * 1024 * 1024) {
177 my ($page) = ($address - $PHYS_BASE) >> 12;
178 if (defined $threads{$page}) {
179 my ($pg_ofs) = $address & 0xfff;
180 return sprintf ("in thread %x %s", $page,
181 $pg_ofs >= 0x800 ? "stack" : "data");
190 for my $dir (split (':', $ENV{PATH})) {
191 my ($file) = "$dir/$target";
192 return $file if -e $file;
200 open (LOG, "<", $checkbochs_log) or die "$checkbochs_log: open: $!\n";
201 my ($min_loc, $max_loc);
206 if (my ($loc) = /^Warning on location (.*):$/) {
212 if ($bts ne $last_bts || hex ($loc) != hex ($min_loc) + 1) {
213 last if defined ($nth) && $nth == $count;
214 $min_loc = $max_loc = $loc;
224 return ($last_bts, $min_loc, $max_loc) if defined ($nth) && $nth == $count;
229 my ($set1, $set2) = @_;
231 $set{$_}++ foreach @$set1;
232 $set{$_}++ foreach @$set2;
234 $set{$_} == 2 && push (@out, $_) foreach keys %set;
235 return sort {$a <=> $b} @out;
239 my ($name, $empty, @locks) = @_;
244 foreach my $lock (@locks) {
245 if ($lock == 0xffff0001) {
247 } elsif ($lock == 0xffff0002) {
253 print " (interrupts disabled)" if $if;
254 print " (LOCK prefix)" if $lock_pfx;