xref: /linux/scripts/markup_oops.pl (revision c19ef7fd8e534c870166213e9e30de9c44b34a76)
15aea50b5SArjan van de Ven#!/usr/bin/perl -w
25aea50b5SArjan van de Ven
3d32ad102SArjan van de Venuse File::Basename;
4d32ad102SArjan van de Ven
55aea50b5SArjan van de Ven# Copyright 2008, Intel Corporation
65aea50b5SArjan van de Ven#
75aea50b5SArjan van de Ven# This file is part of the Linux kernel
85aea50b5SArjan van de Ven#
95aea50b5SArjan van de Ven# This program file is free software; you can redistribute it and/or modify it
105aea50b5SArjan van de Ven# under the terms of the GNU General Public License as published by the
115aea50b5SArjan van de Ven# Free Software Foundation; version 2 of the License.
125aea50b5SArjan van de Ven#
135aea50b5SArjan van de Ven# Authors:
145aea50b5SArjan van de Ven# 	Arjan van de Ven <arjan@linux.intel.com>
155aea50b5SArjan van de Ven
165aea50b5SArjan van de Ven
175aea50b5SArjan van de Venmy $vmlinux_name = $ARGV[0];
18d32ad102SArjan van de Venif (!defined($vmlinux_name)) {
19d32ad102SArjan van de Ven	my $kerver = `uname -r`;
20d32ad102SArjan van de Ven	chomp($kerver);
21d32ad102SArjan van de Ven	$vmlinux_name = "/lib/modules/$kerver/build/vmlinux";
22d32ad102SArjan van de Ven	print "No vmlinux specified, assuming $vmlinux_name\n";
23d32ad102SArjan van de Ven}
24d32ad102SArjan van de Venmy $filename = $vmlinux_name;
255aea50b5SArjan van de Ven#
265aea50b5SArjan van de Ven# Step 1: Parse the oops to find the EIP value
275aea50b5SArjan van de Ven#
285aea50b5SArjan van de Ven
295aea50b5SArjan van de Venmy $target = "0";
30d32ad102SArjan van de Venmy $function;
31d32ad102SArjan van de Venmy $module = "";
32d32ad102SArjan van de Venmy $func_offset;
33d32ad102SArjan van de Venmy $vmaoffset = 0;
34d32ad102SArjan van de Ven
35*c19ef7fdSArjan van de Venmy %regs;
36*c19ef7fdSArjan van de Ven
37*c19ef7fdSArjan van de Ven
38*c19ef7fdSArjan van de Vensub parse_x86_regs
39*c19ef7fdSArjan van de Ven{
40*c19ef7fdSArjan van de Ven	my ($line) = @_;
41*c19ef7fdSArjan van de Ven	if ($line =~ /EAX: ([0-9a-f]+) EBX: ([0-9a-f]+) ECX: ([0-9a-f]+) EDX: ([0-9a-f]+)/) {
42*c19ef7fdSArjan van de Ven		$regs{"%eax"} = $1;
43*c19ef7fdSArjan van de Ven		$regs{"%ebx"} = $2;
44*c19ef7fdSArjan van de Ven		$regs{"%ecx"} = $3;
45*c19ef7fdSArjan van de Ven		$regs{"%edx"} = $4;
46*c19ef7fdSArjan van de Ven	}
47*c19ef7fdSArjan van de Ven	if ($line =~ /ESI: ([0-9a-f]+) EDI: ([0-9a-f]+) EBP: ([0-9a-f]+) ESP: ([0-9a-f]+)/) {
48*c19ef7fdSArjan van de Ven		$regs{"%esi"} = $1;
49*c19ef7fdSArjan van de Ven		$regs{"%edi"} = $2;
50*c19ef7fdSArjan van de Ven		$regs{"%esp"} = $4;
51*c19ef7fdSArjan van de Ven	}
52*c19ef7fdSArjan van de Ven}
53*c19ef7fdSArjan van de Ven
54*c19ef7fdSArjan van de Vensub process_x86_regs
55*c19ef7fdSArjan van de Ven{
56*c19ef7fdSArjan van de Ven	my ($line, $cntr) = @_;
57*c19ef7fdSArjan van de Ven	my $str = "";
58*c19ef7fdSArjan van de Ven	if (length($line) < 40) {
59*c19ef7fdSArjan van de Ven		return ""; # not an asm istruction
60*c19ef7fdSArjan van de Ven	}
61*c19ef7fdSArjan van de Ven
62*c19ef7fdSArjan van de Ven	# find the arguments to the instruction
63*c19ef7fdSArjan van de Ven	if ($line =~ /([0-9a-zA-Z\,\%\(\)\-\+]+)$/) {
64*c19ef7fdSArjan van de Ven		$lastword = $1;
65*c19ef7fdSArjan van de Ven	} else {
66*c19ef7fdSArjan van de Ven		return "";
67*c19ef7fdSArjan van de Ven	}
68*c19ef7fdSArjan van de Ven
69*c19ef7fdSArjan van de Ven	# we need to find the registers that get clobbered,
70*c19ef7fdSArjan van de Ven	# since their value is no longer relevant for previous
71*c19ef7fdSArjan van de Ven	# instructions in the stream.
72*c19ef7fdSArjan van de Ven
73*c19ef7fdSArjan van de Ven	$clobber = $lastword;
74*c19ef7fdSArjan van de Ven	# first, remove all memory operands, they're read only
75*c19ef7fdSArjan van de Ven	$clobber =~ s/\([a-z0-9\%\,]+\)//g;
76*c19ef7fdSArjan van de Ven	# then, remove everything before the comma, thats the read part
77*c19ef7fdSArjan van de Ven	$clobber =~ s/.*\,//g;
78*c19ef7fdSArjan van de Ven
79*c19ef7fdSArjan van de Ven	# if this is the instruction that faulted, we haven't actually done
80*c19ef7fdSArjan van de Ven	# the write yet... nothing is clobbered.
81*c19ef7fdSArjan van de Ven	if ($cntr == 0) {
82*c19ef7fdSArjan van de Ven		$clobber = "";
83*c19ef7fdSArjan van de Ven	}
84*c19ef7fdSArjan van de Ven
85*c19ef7fdSArjan van de Ven	foreach $reg (keys(%regs)) {
86*c19ef7fdSArjan van de Ven		my $val = $regs{$reg};
87*c19ef7fdSArjan van de Ven		# first check if we're clobbering this register; if we do
88*c19ef7fdSArjan van de Ven		# we print it with a =>, and then delete its value
89*c19ef7fdSArjan van de Ven		if ($clobber =~ /$reg/) {
90*c19ef7fdSArjan van de Ven			if (length($val) > 0) {
91*c19ef7fdSArjan van de Ven				$str = $str . " $reg => $val ";
92*c19ef7fdSArjan van de Ven			}
93*c19ef7fdSArjan van de Ven			$regs{$reg} = "";
94*c19ef7fdSArjan van de Ven			$val = "";
95*c19ef7fdSArjan van de Ven		}
96*c19ef7fdSArjan van de Ven		# now check if we're reading this register
97*c19ef7fdSArjan van de Ven		if ($lastword =~ /$reg/) {
98*c19ef7fdSArjan van de Ven			if (length($val) > 0) {
99*c19ef7fdSArjan van de Ven				$str = $str . " $reg = $val ";
100*c19ef7fdSArjan van de Ven			}
101*c19ef7fdSArjan van de Ven		}
102*c19ef7fdSArjan van de Ven	}
103*c19ef7fdSArjan van de Ven	return $str;
104*c19ef7fdSArjan van de Ven}
105*c19ef7fdSArjan van de Ven
106*c19ef7fdSArjan van de Ven# parse the oops
1075aea50b5SArjan van de Venwhile (<STDIN>) {
108d32ad102SArjan van de Ven	my $line = $_;
109d32ad102SArjan van de Ven	if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
1105aea50b5SArjan van de Ven		$target = $1;
1115aea50b5SArjan van de Ven	}
112d32ad102SArjan van de Ven	if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]/) {
113d32ad102SArjan van de Ven		$function = $1;
114d32ad102SArjan van de Ven		$func_offset = $2;
1155aea50b5SArjan van de Ven	}
1165aea50b5SArjan van de Ven
117d32ad102SArjan van de Ven	# check if it's a module
118d32ad102SArjan van de Ven	if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
119d32ad102SArjan van de Ven		$module = $3;
120d32ad102SArjan van de Ven	}
121*c19ef7fdSArjan van de Ven	parse_x86_regs($line);
1225aea50b5SArjan van de Ven}
1235aea50b5SArjan van de Ven
124d32ad102SArjan van de Venmy $decodestart = hex($target) - hex($func_offset);
125*c19ef7fdSArjan van de Venmy $decodestop = hex($target) + 8192;
1265aea50b5SArjan van de Venif ($target eq "0") {
1275aea50b5SArjan van de Ven	print "No oops found!\n";
1285aea50b5SArjan van de Ven	print "Usage: \n";
1295aea50b5SArjan van de Ven	print "    dmesg | perl scripts/markup_oops.pl vmlinux\n";
1305aea50b5SArjan van de Ven	exit;
1315aea50b5SArjan van de Ven}
1325aea50b5SArjan van de Ven
133d32ad102SArjan van de Ven# if it's a module, we need to find the .ko file and calculate a load offset
134d32ad102SArjan van de Venif ($module ne "") {
135d32ad102SArjan van de Ven	my $dir = dirname($filename);
136d32ad102SArjan van de Ven	$dir = $dir . "/";
137d32ad102SArjan van de Ven	my $mod = $module . ".ko";
138d32ad102SArjan van de Ven	my $modulefile = `find $dir -name $mod | head -1`;
139d32ad102SArjan van de Ven	chomp($modulefile);
140d32ad102SArjan van de Ven	$filename = $modulefile;
141d32ad102SArjan van de Ven	if ($filename eq "") {
142d32ad102SArjan van de Ven		print "Module .ko file for $module not found. Aborting\n";
143d32ad102SArjan van de Ven		exit;
144d32ad102SArjan van de Ven	}
145d32ad102SArjan van de Ven	# ok so we found the module, now we need to calculate the vma offset
146d32ad102SArjan van de Ven	open(FILE, "objdump -dS $filename |") || die "Cannot start objdump";
147d32ad102SArjan van de Ven	while (<FILE>) {
148d32ad102SArjan van de Ven		if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) {
149d32ad102SArjan van de Ven			my $fu = $1;
150d32ad102SArjan van de Ven			$vmaoffset = hex($target) - hex($fu) - hex($func_offset);
151d32ad102SArjan van de Ven		}
152d32ad102SArjan van de Ven	}
153d32ad102SArjan van de Ven	close(FILE);
154d32ad102SArjan van de Ven}
155d32ad102SArjan van de Ven
1565aea50b5SArjan van de Venmy $counter = 0;
1575aea50b5SArjan van de Venmy $state   = 0;
1585aea50b5SArjan van de Venmy $center  = 0;
1595aea50b5SArjan van de Venmy @lines;
160*c19ef7fdSArjan van de Venmy @reglines;
1615aea50b5SArjan van de Ven
1625aea50b5SArjan van de Vensub InRange {
1635aea50b5SArjan van de Ven	my ($address, $target) = @_;
1645aea50b5SArjan van de Ven	my $ad = "0x".$address;
1655aea50b5SArjan van de Ven	my $ta = "0x".$target;
1665aea50b5SArjan van de Ven	my $delta = hex($ad) - hex($ta);
1675aea50b5SArjan van de Ven
1685aea50b5SArjan van de Ven	if (($delta > -4096) && ($delta < 4096)) {
1695aea50b5SArjan van de Ven		return 1;
1705aea50b5SArjan van de Ven	}
1715aea50b5SArjan van de Ven	return 0;
1725aea50b5SArjan van de Ven}
1735aea50b5SArjan van de Ven
1745aea50b5SArjan van de Ven
1755aea50b5SArjan van de Ven
1765aea50b5SArjan van de Ven# first, parse the input into the lines array, but to keep size down,
1775aea50b5SArjan van de Ven# we only do this for 4Kb around the sweet spot
1785aea50b5SArjan van de Ven
179d32ad102SArjan van de Venopen(FILE, "objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump";
1805aea50b5SArjan van de Ven
1815aea50b5SArjan van de Venwhile (<FILE>) {
1825aea50b5SArjan van de Ven	my $line = $_;
1835aea50b5SArjan van de Ven	chomp($line);
1845aea50b5SArjan van de Ven	if ($state == 0) {
1855aea50b5SArjan van de Ven		if ($line =~ /^([a-f0-9]+)\:/) {
1865aea50b5SArjan van de Ven			if (InRange($1, $target)) {
1875aea50b5SArjan van de Ven				$state = 1;
1885aea50b5SArjan van de Ven			}
1895aea50b5SArjan van de Ven		}
1905aea50b5SArjan van de Ven	} else {
1915aea50b5SArjan van de Ven		if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) {
1925aea50b5SArjan van de Ven			my $val = $1;
1935aea50b5SArjan van de Ven			if (!InRange($val, $target)) {
1945aea50b5SArjan van de Ven				last;
1955aea50b5SArjan van de Ven			}
1965aea50b5SArjan van de Ven			if ($val eq $target) {
1975aea50b5SArjan van de Ven				$center = $counter;
1985aea50b5SArjan van de Ven			}
1995aea50b5SArjan van de Ven		}
2005aea50b5SArjan van de Ven		$lines[$counter] = $line;
2015aea50b5SArjan van de Ven
2025aea50b5SArjan van de Ven		$counter = $counter + 1;
2035aea50b5SArjan van de Ven	}
2045aea50b5SArjan van de Ven}
2055aea50b5SArjan van de Ven
2065aea50b5SArjan van de Venclose(FILE);
2075aea50b5SArjan van de Ven
2085aea50b5SArjan van de Venif ($counter == 0) {
2095aea50b5SArjan van de Ven	print "No matching code found \n";
2105aea50b5SArjan van de Ven	exit;
2115aea50b5SArjan van de Ven}
2125aea50b5SArjan van de Ven
2135aea50b5SArjan van de Venif ($center == 0) {
2145aea50b5SArjan van de Ven	print "No matching code found \n";
2155aea50b5SArjan van de Ven	exit;
2165aea50b5SArjan van de Ven}
2175aea50b5SArjan van de Ven
2185aea50b5SArjan van de Venmy $start;
2195aea50b5SArjan van de Venmy $finish;
2205aea50b5SArjan van de Venmy $codelines = 0;
2215aea50b5SArjan van de Venmy $binarylines = 0;
2225aea50b5SArjan van de Ven# now we go up and down in the array to find how much we want to print
2235aea50b5SArjan van de Ven
2245aea50b5SArjan van de Ven$start = $center;
2255aea50b5SArjan van de Ven
2265aea50b5SArjan van de Venwhile ($start > 1) {
2275aea50b5SArjan van de Ven	$start = $start - 1;
2285aea50b5SArjan van de Ven	my $line = $lines[$start];
2295aea50b5SArjan van de Ven	if ($line =~ /^([a-f0-9]+)\:/) {
2305aea50b5SArjan van de Ven		$binarylines = $binarylines + 1;
2315aea50b5SArjan van de Ven	} else {
2325aea50b5SArjan van de Ven		$codelines = $codelines + 1;
2335aea50b5SArjan van de Ven	}
2345aea50b5SArjan van de Ven	if ($codelines > 10) {
2355aea50b5SArjan van de Ven		last;
2365aea50b5SArjan van de Ven	}
2375aea50b5SArjan van de Ven	if ($binarylines > 20) {
2385aea50b5SArjan van de Ven		last;
2395aea50b5SArjan van de Ven	}
2405aea50b5SArjan van de Ven}
2415aea50b5SArjan van de Ven
2425aea50b5SArjan van de Ven
2435aea50b5SArjan van de Ven$finish = $center;
2445aea50b5SArjan van de Ven$codelines = 0;
2455aea50b5SArjan van de Ven$binarylines = 0;
2465aea50b5SArjan van de Venwhile ($finish < $counter) {
2475aea50b5SArjan van de Ven	$finish = $finish + 1;
2485aea50b5SArjan van de Ven	my $line = $lines[$finish];
2495aea50b5SArjan van de Ven	if ($line =~ /^([a-f0-9]+)\:/) {
2505aea50b5SArjan van de Ven		$binarylines = $binarylines + 1;
2515aea50b5SArjan van de Ven	} else {
2525aea50b5SArjan van de Ven		$codelines = $codelines + 1;
2535aea50b5SArjan van de Ven	}
2545aea50b5SArjan van de Ven	if ($codelines > 10) {
2555aea50b5SArjan van de Ven		last;
2565aea50b5SArjan van de Ven	}
2575aea50b5SArjan van de Ven	if ($binarylines > 20) {
2585aea50b5SArjan van de Ven		last;
2595aea50b5SArjan van de Ven	}
2605aea50b5SArjan van de Ven}
2615aea50b5SArjan van de Ven
2625aea50b5SArjan van de Ven
2635aea50b5SArjan van de Venmy $i;
2645aea50b5SArjan van de Ven
265*c19ef7fdSArjan van de Ven
266*c19ef7fdSArjan van de Ven# start annotating the registers in the asm.
267*c19ef7fdSArjan van de Ven# this goes from the oopsing point back, so that the annotator
268*c19ef7fdSArjan van de Ven# can track (opportunistically) which registers got written and
269*c19ef7fdSArjan van de Ven# whos value no longer is relevant.
270*c19ef7fdSArjan van de Ven
271*c19ef7fdSArjan van de Ven$i = $center;
272*c19ef7fdSArjan van de Venwhile ($i >= $start) {
273*c19ef7fdSArjan van de Ven	$reglines[$i] = process_x86_regs($lines[$i], $center - $i);
274*c19ef7fdSArjan van de Ven	$i = $i - 1;
2755aea50b5SArjan van de Ven}
2765aea50b5SArjan van de Ven
277*c19ef7fdSArjan van de Ven$i = $start;
278*c19ef7fdSArjan van de Venwhile ($i < $finish) {
279*c19ef7fdSArjan van de Ven	my $line;
280*c19ef7fdSArjan van de Ven	if ($i == $center) {
281*c19ef7fdSArjan van de Ven		$line =  "*$lines[$i] ";
282*c19ef7fdSArjan van de Ven	} else {
283*c19ef7fdSArjan van de Ven		$line =  " $lines[$i] ";
284*c19ef7fdSArjan van de Ven	}
285*c19ef7fdSArjan van de Ven	print $line;
286*c19ef7fdSArjan van de Ven	if (defined($reglines[$i]) && length($reglines[$i]) > 0) {
287*c19ef7fdSArjan van de Ven		my $c = 60 - length($line);
288*c19ef7fdSArjan van de Ven		while ($c > 0) { print " "; $c = $c - 1; };
289*c19ef7fdSArjan van de Ven		print "| $reglines[$i]";
290*c19ef7fdSArjan van de Ven	}
291*c19ef7fdSArjan van de Ven	if ($i == $center) {
292*c19ef7fdSArjan van de Ven		print "<--- faulting instruction";
293*c19ef7fdSArjan van de Ven	}
294*c19ef7fdSArjan van de Ven	print "\n";
295*c19ef7fdSArjan van de Ven	$i = $i +1;
296*c19ef7fdSArjan van de Ven}
2975aea50b5SArjan van de Ven
298