Blame view
scripts/markup_oops.pl
7.92 KB
cb77f0d62 scripts: Switch t... |
1 |
#!/usr/bin/env perl |
76e692f50 treewide: Replace... |
2 |
# SPDX-License-Identifier: GPL-2.0-only |
5aea50b5c scripts: script f... |
3 |
|
d32ad102c script: improve m... |
4 |
use File::Basename; |
51fbb4bab markup_oops: fix ... |
5 |
use Math::BigInt; |
52e13e219 markup_oops.pl: a... |
6 |
use Getopt::Long; |
d32ad102c script: improve m... |
7 |
|
5aea50b5c scripts: script f... |
8 9 10 11 |
# Copyright 2008, Intel Corporation # # This file is part of the Linux kernel # |
5aea50b5c scripts: script f... |
12 13 |
# Authors: # Arjan van de Ven <arjan@linux.intel.com> |
52e13e219 markup_oops.pl: a... |
14 15 16 17 18 19 20 |
my $cross_compile = ""; my $vmlinux_name = ""; my $modulefile = ""; # Get options Getopt::Long::GetOptions( 'cross-compile|c=s' => \$cross_compile, |
59dde3853 markup_oops.pl: m... |
21 |
'module|m=s' => \$modulefile, |
52e13e219 markup_oops.pl: a... |
22 |
'help|h' => \&usage, |
59dde3853 markup_oops.pl: m... |
23 |
) || usage (); |
5aea50b5c scripts: script f... |
24 |
my $vmlinux_name = $ARGV[0]; |
d32ad102c script: improve m... |
25 26 27 28 29 30 31 32 |
if (!defined($vmlinux_name)) { my $kerver = `uname -r`; chomp($kerver); $vmlinux_name = "/lib/modules/$kerver/build/vmlinux"; print "No vmlinux specified, assuming $vmlinux_name "; } my $filename = $vmlinux_name; |
52e13e219 markup_oops.pl: a... |
33 34 |
# Parse the oops to find the EIP value |
5aea50b5c scripts: script f... |
35 36 |
my $target = "0"; |
d32ad102c script: improve m... |
37 38 |
my $function; my $module = ""; |
11df65c3c scripts: add x86 ... |
39 |
my $func_offset = 0; |
d32ad102c script: improve m... |
40 |
my $vmaoffset = 0; |
c19ef7fd8 scripts: add x86 ... |
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
my %regs; sub parse_x86_regs { my ($line) = @_; if ($line =~ /EAX: ([0-9a-f]+) EBX: ([0-9a-f]+) ECX: ([0-9a-f]+) EDX: ([0-9a-f]+)/) { $regs{"%eax"} = $1; $regs{"%ebx"} = $2; $regs{"%ecx"} = $3; $regs{"%edx"} = $4; } if ($line =~ /ESI: ([0-9a-f]+) EDI: ([0-9a-f]+) EBP: ([0-9a-f]+) ESP: ([0-9a-f]+)/) { $regs{"%esi"} = $1; $regs{"%edi"} = $2; $regs{"%esp"} = $4; } |
11df65c3c scripts: add x86 ... |
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
if ($line =~ /RAX: ([0-9a-f]+) RBX: ([0-9a-f]+) RCX: ([0-9a-f]+)/) { $regs{"%eax"} = $1; $regs{"%ebx"} = $2; $regs{"%ecx"} = $3; } if ($line =~ /RDX: ([0-9a-f]+) RSI: ([0-9a-f]+) RDI: ([0-9a-f]+)/) { $regs{"%edx"} = $1; $regs{"%esi"} = $2; $regs{"%edi"} = $3; } if ($line =~ /RBP: ([0-9a-f]+) R08: ([0-9a-f]+) R09: ([0-9a-f]+)/) { $regs{"%r08"} = $2; $regs{"%r09"} = $3; } if ($line =~ /R10: ([0-9a-f]+) R11: ([0-9a-f]+) R12: ([0-9a-f]+)/) { $regs{"%r10"} = $1; $regs{"%r11"} = $2; $regs{"%r12"} = $3; } if ($line =~ /R13: ([0-9a-f]+) R14: ([0-9a-f]+) R15: ([0-9a-f]+)/) { $regs{"%r13"} = $1; $regs{"%r14"} = $2; $regs{"%r15"} = $3; } } sub reg_name { my ($reg) = @_; $reg =~ s/r(.)x/e\1x/; $reg =~ s/r(.)i/e\1i/; $reg =~ s/r(.)p/e\1p/; return $reg; |
c19ef7fd8 scripts: add x86 ... |
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
} sub process_x86_regs { my ($line, $cntr) = @_; my $str = ""; if (length($line) < 40) { return ""; # not an asm istruction } # find the arguments to the instruction if ($line =~ /([0-9a-zA-Z\,\%\(\)\-\+]+)$/) { $lastword = $1; } else { return ""; } # we need to find the registers that get clobbered, # since their value is no longer relevant for previous # instructions in the stream. $clobber = $lastword; # first, remove all memory operands, they're read only $clobber =~ s/\([a-z0-9\%\,]+\)//g; # then, remove everything before the comma, thats the read part $clobber =~ s/.*\,//g; # if this is the instruction that faulted, we haven't actually done # the write yet... nothing is clobbered. if ($cntr == 0) { $clobber = ""; } foreach $reg (keys(%regs)) { |
11df65c3c scripts: add x86 ... |
125 126 |
my $clobberprime = reg_name($clobber); my $lastwordprime = reg_name($lastword); |
c19ef7fd8 scripts: add x86 ... |
127 |
my $val = $regs{$reg}; |
11df65c3c scripts: add x86 ... |
128 129 130 131 132 |
if ($val =~ /^[0]+$/) { $val = "0"; } else { $val =~ s/^0*//; } |
c19ef7fd8 scripts: add x86 ... |
133 134 |
# first check if we're clobbering this register; if we do # we print it with a =>, and then delete its value |
11df65c3c scripts: add x86 ... |
135 |
if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) { |
c19ef7fd8 scripts: add x86 ... |
136 137 138 139 140 141 142 |
if (length($val) > 0) { $str = $str . " $reg => $val "; } $regs{$reg} = ""; $val = ""; } # now check if we're reading this register |
11df65c3c scripts: add x86 ... |
143 |
if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) { |
c19ef7fd8 scripts: add x86 ... |
144 145 146 147 148 149 150 151 152 |
if (length($val) > 0) { $str = $str . " $reg = $val "; } } } return $str; } # parse the oops |
5aea50b5c scripts: script f... |
153 |
while (<STDIN>) { |
d32ad102c script: improve m... |
154 155 |
my $line = $_; if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) { |
5aea50b5c scripts: script f... |
156 157 |
$target = $1; } |
11df65c3c scripts: add x86 ... |
158 159 160 |
if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) { $target = $1; } |
1f8cdae43 markup_oops.pl: f... |
161 |
if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) { |
d32ad102c script: improve m... |
162 163 164 |
$function = $1; $func_offset = $2; } |
ef2b9b054 markup_oops.pl: f... |
165 |
if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\] \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) { |
11df65c3c scripts: add x86 ... |
166 167 168 |
$function = $1; $func_offset = $2; } |
5aea50b5c scripts: script f... |
169 |
|
d32ad102c script: improve m... |
170 171 172 173 |
# check if it's a module if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) { $module = $3; } |
11df65c3c scripts: add x86 ... |
174 175 176 |
if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\] \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) { $module = $3; } |
c19ef7fd8 scripts: add x86 ... |
177 |
parse_x86_regs($line); |
5aea50b5c scripts: script f... |
178 |
} |
51fbb4bab markup_oops: fix ... |
179 180 |
my $decodestart = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$func_offset"); my $decodestop = Math::BigInt->from_hex("0x$target") + 8192; |
5aea50b5c scripts: script f... |
181 182 183 |
if ($target eq "0") { print "No oops found! "; |
52e13e219 markup_oops.pl: a... |
184 |
usage(); |
5aea50b5c scripts: script f... |
185 |
} |
d32ad102c script: improve m... |
186 187 |
# if it's a module, we need to find the .ko file and calculate a load offset if ($module ne "") { |
52e13e219 markup_oops.pl: a... |
188 |
if ($modulefile eq "") { |
59dde3853 markup_oops.pl: m... |
189 |
$modulefile = `modinfo -F filename $module`; |
52e13e219 markup_oops.pl: a... |
190 191 |
chomp($modulefile); } |
d32ad102c script: improve m... |
192 193 194 195 196 197 198 |
$filename = $modulefile; if ($filename eq "") { print "Module .ko file for $module not found. Aborting "; exit; } # ok so we found the module, now we need to calculate the vma offset |
52e13e219 markup_oops.pl: a... |
199 |
open(FILE, $cross_compile."objdump -dS $filename |") || die "Cannot start objdump"; |
d32ad102c script: improve m... |
200 201 202 |
while (<FILE>) { if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) { my $fu = $1; |
52e13e219 markup_oops.pl: a... |
203 |
$vmaoffset = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$fu") - Math::BigInt->from_hex("0x$func_offset"); |
d32ad102c script: improve m... |
204 205 206 207 |
} } close(FILE); } |
5aea50b5c scripts: script f... |
208 209 |
my $counter = 0; my $state = 0; |
0139f1d95 markup_oops.pl: f... |
210 |
my $center = -1; |
5aea50b5c scripts: script f... |
211 |
my @lines; |
c19ef7fd8 scripts: add x86 ... |
212 |
my @reglines; |
5aea50b5c scripts: script f... |
213 214 215 216 217 |
sub InRange { my ($address, $target) = @_; my $ad = "0x".$address; my $ta = "0x".$target; |
52e13e219 markup_oops.pl: a... |
218 |
my $delta = Math::BigInt->from_hex($ad) - Math::BigInt->from_hex($ta); |
5aea50b5c scripts: script f... |
219 220 221 222 223 224 225 226 227 228 229 |
if (($delta > -4096) && ($delta < 4096)) { return 1; } return 0; } # first, parse the input into the lines array, but to keep size down, # we only do this for 4Kb around the sweet spot |
52e13e219 markup_oops.pl: a... |
230 |
open(FILE, $cross_compile."objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump"; |
5aea50b5c scripts: script f... |
231 232 233 234 235 236 237 238 239 240 |
while (<FILE>) { my $line = $_; chomp($line); if ($state == 0) { if ($line =~ /^([a-f0-9]+)\:/) { if (InRange($1, $target)) { $state = 1; } } |
0139f1d95 markup_oops.pl: f... |
241 242 |
} if ($state == 1) { |
5aea50b5c scripts: script f... |
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) { my $val = $1; if (!InRange($val, $target)) { last; } if ($val eq $target) { $center = $counter; } } $lines[$counter] = $line; $counter = $counter + 1; } } close(FILE); if ($counter == 0) { print "No matching code found "; exit; } |
0139f1d95 markup_oops.pl: f... |
265 |
if ($center == -1) { |
5aea50b5c scripts: script f... |
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
print "No matching code found "; exit; } my $start; my $finish; my $codelines = 0; my $binarylines = 0; # now we go up and down in the array to find how much we want to print $start = $center; while ($start > 1) { $start = $start - 1; my $line = $lines[$start]; if ($line =~ /^([a-f0-9]+)\:/) { $binarylines = $binarylines + 1; } else { $codelines = $codelines + 1; } if ($codelines > 10) { last; } if ($binarylines > 20) { last; } } $finish = $center; $codelines = 0; $binarylines = 0; while ($finish < $counter) { $finish = $finish + 1; my $line = $lines[$finish]; if ($line =~ /^([a-f0-9]+)\:/) { $binarylines = $binarylines + 1; } else { $codelines = $codelines + 1; } if ($codelines > 10) { last; } if ($binarylines > 20) { last; } } my $i; |
c19ef7fd8 scripts: add x86 ... |
317 318 319 320 321 322 323 324 325 326 327 |
# start annotating the registers in the asm. # this goes from the oopsing point back, so that the annotator # can track (opportunistically) which registers got written and # whos value no longer is relevant. $i = $center; while ($i >= $start) { $reglines[$i] = process_x86_regs($lines[$i], $center - $i); $i = $i - 1; } |
5aea50b5c scripts: script f... |
328 329 |
$i = $start; while ($i < $finish) { |
c19ef7fd8 scripts: add x86 ... |
330 |
my $line; |
5aea50b5c scripts: script f... |
331 |
if ($i == $center) { |
c19ef7fd8 scripts: add x86 ... |
332 |
$line = "*$lines[$i] "; |
5aea50b5c scripts: script f... |
333 |
} else { |
c19ef7fd8 scripts: add x86 ... |
334 335 336 337 338 339 340 |
$line = " $lines[$i] "; } print $line; if (defined($reglines[$i]) && length($reglines[$i]) > 0) { my $c = 60 - length($line); while ($c > 0) { print " "; $c = $c - 1; }; print "| $reglines[$i]"; |
5aea50b5c scripts: script f... |
341 |
} |
c19ef7fd8 scripts: add x86 ... |
342 343 344 345 346 |
if ($i == $center) { print "<--- faulting instruction"; } print " "; |
5aea50b5c scripts: script f... |
347 348 |
$i = $i +1; } |
52e13e219 markup_oops.pl: a... |
349 350 351 352 353 354 355 |
sub usage { print <<EOT; Usage: dmesg | perl $0 [OPTION] [VMLINUX] OPTION: -c, --cross-compile CROSS_COMPILE Specify the prefix used for toolchain. |
59dde3853 markup_oops.pl: m... |
356 |
-m, --module MODULE_DIRNAME Specify the module filename. |
52e13e219 markup_oops.pl: a... |
357 358 359 360 |
-h, --help Help. EOT exit; } |