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