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