1cb77f0d6SKamil Rytarowski#!/usr/bin/env perl 2*76e692f5SThomas Gleixner# SPDX-License-Identifier: GPL-2.0-only 35aea50b5SArjan van de Ven 4d32ad102SArjan van de Venuse File::Basename; 551fbb4baSMatthew Wilcoxuse Math::BigInt; 652e13e21SHui Zhuuse Getopt::Long; 7d32ad102SArjan van de Ven 85aea50b5SArjan van de Ven# Copyright 2008, Intel Corporation 95aea50b5SArjan van de Ven# 105aea50b5SArjan van de Ven# This file is part of the Linux kernel 115aea50b5SArjan van de Ven# 125aea50b5SArjan van de Ven# Authors: 135aea50b5SArjan van de Ven# Arjan van de Ven <arjan@linux.intel.com> 145aea50b5SArjan van de Ven 155aea50b5SArjan van de Ven 1652e13e21SHui Zhumy $cross_compile = ""; 1752e13e21SHui Zhumy $vmlinux_name = ""; 1852e13e21SHui Zhumy $modulefile = ""; 1952e13e21SHui Zhu 2052e13e21SHui Zhu# Get options 2152e13e21SHui ZhuGetopt::Long::GetOptions( 2252e13e21SHui Zhu 'cross-compile|c=s' => \$cross_compile, 2352e13e21SHui Zhu 'module|m=s' => \$modulefile, 2452e13e21SHui Zhu 'help|h' => \&usage, 2559dde385SHui Zhu) || usage (); 265aea50b5SArjan van de Venmy $vmlinux_name = $ARGV[0]; 27d32ad102SArjan van de Venif (!defined($vmlinux_name)) { 28d32ad102SArjan van de Ven my $kerver = `uname -r`; 29d32ad102SArjan van de Ven chomp($kerver); 30d32ad102SArjan van de Ven $vmlinux_name = "/lib/modules/$kerver/build/vmlinux"; 31d32ad102SArjan van de Ven print "No vmlinux specified, assuming $vmlinux_name\n"; 32d32ad102SArjan van de Ven} 33d32ad102SArjan van de Venmy $filename = $vmlinux_name; 3452e13e21SHui Zhu 3552e13e21SHui Zhu# Parse the oops to find the EIP value 365aea50b5SArjan van de Ven 375aea50b5SArjan van de Venmy $target = "0"; 38d32ad102SArjan van de Venmy $function; 39d32ad102SArjan van de Venmy $module = ""; 4011df65c3SArjan van de Venmy $func_offset = 0; 41d32ad102SArjan van de Venmy $vmaoffset = 0; 42d32ad102SArjan van de Ven 43c19ef7fdSArjan van de Venmy %regs; 44c19ef7fdSArjan van de Ven 45c19ef7fdSArjan van de Ven 46c19ef7fdSArjan van de Vensub parse_x86_regs 47c19ef7fdSArjan van de Ven{ 48c19ef7fdSArjan van de Ven my ($line) = @_; 49c19ef7fdSArjan van de Ven if ($line =~ /EAX: ([0-9a-f]+) EBX: ([0-9a-f]+) ECX: ([0-9a-f]+) EDX: ([0-9a-f]+)/) { 50c19ef7fdSArjan van de Ven $regs{"%eax"} = $1; 51c19ef7fdSArjan van de Ven $regs{"%ebx"} = $2; 52c19ef7fdSArjan van de Ven $regs{"%ecx"} = $3; 53c19ef7fdSArjan van de Ven $regs{"%edx"} = $4; 54c19ef7fdSArjan van de Ven } 55c19ef7fdSArjan van de Ven if ($line =~ /ESI: ([0-9a-f]+) EDI: ([0-9a-f]+) EBP: ([0-9a-f]+) ESP: ([0-9a-f]+)/) { 56c19ef7fdSArjan van de Ven $regs{"%esi"} = $1; 57c19ef7fdSArjan van de Ven $regs{"%edi"} = $2; 58c19ef7fdSArjan van de Ven $regs{"%esp"} = $4; 59c19ef7fdSArjan van de Ven } 6011df65c3SArjan van de Ven if ($line =~ /RAX: ([0-9a-f]+) RBX: ([0-9a-f]+) RCX: ([0-9a-f]+)/) { 6111df65c3SArjan van de Ven $regs{"%eax"} = $1; 6211df65c3SArjan van de Ven $regs{"%ebx"} = $2; 6311df65c3SArjan van de Ven $regs{"%ecx"} = $3; 6411df65c3SArjan van de Ven } 6511df65c3SArjan van de Ven if ($line =~ /RDX: ([0-9a-f]+) RSI: ([0-9a-f]+) RDI: ([0-9a-f]+)/) { 6611df65c3SArjan van de Ven $regs{"%edx"} = $1; 6711df65c3SArjan van de Ven $regs{"%esi"} = $2; 6811df65c3SArjan van de Ven $regs{"%edi"} = $3; 6911df65c3SArjan van de Ven } 7011df65c3SArjan van de Ven if ($line =~ /RBP: ([0-9a-f]+) R08: ([0-9a-f]+) R09: ([0-9a-f]+)/) { 7111df65c3SArjan van de Ven $regs{"%r08"} = $2; 7211df65c3SArjan van de Ven $regs{"%r09"} = $3; 7311df65c3SArjan van de Ven } 7411df65c3SArjan van de Ven if ($line =~ /R10: ([0-9a-f]+) R11: ([0-9a-f]+) R12: ([0-9a-f]+)/) { 7511df65c3SArjan van de Ven $regs{"%r10"} = $1; 7611df65c3SArjan van de Ven $regs{"%r11"} = $2; 7711df65c3SArjan van de Ven $regs{"%r12"} = $3; 7811df65c3SArjan van de Ven } 7911df65c3SArjan van de Ven if ($line =~ /R13: ([0-9a-f]+) R14: ([0-9a-f]+) R15: ([0-9a-f]+)/) { 8011df65c3SArjan van de Ven $regs{"%r13"} = $1; 8111df65c3SArjan van de Ven $regs{"%r14"} = $2; 8211df65c3SArjan van de Ven $regs{"%r15"} = $3; 8311df65c3SArjan van de Ven } 8411df65c3SArjan van de Ven} 8511df65c3SArjan van de Ven 8611df65c3SArjan van de Vensub reg_name 8711df65c3SArjan van de Ven{ 8811df65c3SArjan van de Ven my ($reg) = @_; 8911df65c3SArjan van de Ven $reg =~ s/r(.)x/e\1x/; 9011df65c3SArjan van de Ven $reg =~ s/r(.)i/e\1i/; 9111df65c3SArjan van de Ven $reg =~ s/r(.)p/e\1p/; 9211df65c3SArjan van de Ven return $reg; 93c19ef7fdSArjan van de Ven} 94c19ef7fdSArjan van de Ven 95c19ef7fdSArjan van de Vensub process_x86_regs 96c19ef7fdSArjan van de Ven{ 97c19ef7fdSArjan van de Ven my ($line, $cntr) = @_; 98c19ef7fdSArjan van de Ven my $str = ""; 99c19ef7fdSArjan van de Ven if (length($line) < 40) { 100c19ef7fdSArjan van de Ven return ""; # not an asm istruction 101c19ef7fdSArjan van de Ven } 102c19ef7fdSArjan van de Ven 103c19ef7fdSArjan van de Ven # find the arguments to the instruction 104c19ef7fdSArjan van de Ven if ($line =~ /([0-9a-zA-Z\,\%\(\)\-\+]+)$/) { 105c19ef7fdSArjan van de Ven $lastword = $1; 106c19ef7fdSArjan van de Ven } else { 107c19ef7fdSArjan van de Ven return ""; 108c19ef7fdSArjan van de Ven } 109c19ef7fdSArjan van de Ven 110c19ef7fdSArjan van de Ven # we need to find the registers that get clobbered, 111c19ef7fdSArjan van de Ven # since their value is no longer relevant for previous 112c19ef7fdSArjan van de Ven # instructions in the stream. 113c19ef7fdSArjan van de Ven 114c19ef7fdSArjan van de Ven $clobber = $lastword; 115c19ef7fdSArjan van de Ven # first, remove all memory operands, they're read only 116c19ef7fdSArjan van de Ven $clobber =~ s/\([a-z0-9\%\,]+\)//g; 117c19ef7fdSArjan van de Ven # then, remove everything before the comma, thats the read part 118c19ef7fdSArjan van de Ven $clobber =~ s/.*\,//g; 119c19ef7fdSArjan van de Ven 120c19ef7fdSArjan van de Ven # if this is the instruction that faulted, we haven't actually done 121c19ef7fdSArjan van de Ven # the write yet... nothing is clobbered. 122c19ef7fdSArjan van de Ven if ($cntr == 0) { 123c19ef7fdSArjan van de Ven $clobber = ""; 124c19ef7fdSArjan van de Ven } 125c19ef7fdSArjan van de Ven 126c19ef7fdSArjan van de Ven foreach $reg (keys(%regs)) { 12711df65c3SArjan van de Ven my $clobberprime = reg_name($clobber); 12811df65c3SArjan van de Ven my $lastwordprime = reg_name($lastword); 129c19ef7fdSArjan van de Ven my $val = $regs{$reg}; 13011df65c3SArjan van de Ven if ($val =~ /^[0]+$/) { 13111df65c3SArjan van de Ven $val = "0"; 13211df65c3SArjan van de Ven } else { 13311df65c3SArjan van de Ven $val =~ s/^0*//; 13411df65c3SArjan van de Ven } 13511df65c3SArjan van de Ven 136c19ef7fdSArjan van de Ven # first check if we're clobbering this register; if we do 137c19ef7fdSArjan van de Ven # we print it with a =>, and then delete its value 13811df65c3SArjan van de Ven if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) { 139c19ef7fdSArjan van de Ven if (length($val) > 0) { 140c19ef7fdSArjan van de Ven $str = $str . " $reg => $val "; 141c19ef7fdSArjan van de Ven } 142c19ef7fdSArjan van de Ven $regs{$reg} = ""; 143c19ef7fdSArjan van de Ven $val = ""; 144c19ef7fdSArjan van de Ven } 145c19ef7fdSArjan van de Ven # now check if we're reading this register 14611df65c3SArjan van de Ven if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) { 147c19ef7fdSArjan van de Ven if (length($val) > 0) { 148c19ef7fdSArjan van de Ven $str = $str . " $reg = $val "; 149c19ef7fdSArjan van de Ven } 150c19ef7fdSArjan van de Ven } 151c19ef7fdSArjan van de Ven } 152c19ef7fdSArjan van de Ven return $str; 153c19ef7fdSArjan van de Ven} 154c19ef7fdSArjan van de Ven 155c19ef7fdSArjan van de Ven# parse the oops 1565aea50b5SArjan van de Venwhile (<STDIN>) { 157d32ad102SArjan van de Ven my $line = $_; 158d32ad102SArjan van de Ven if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) { 1595aea50b5SArjan van de Ven $target = $1; 1605aea50b5SArjan van de Ven } 16111df65c3SArjan van de Ven if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) { 16211df65c3SArjan van de Ven $target = $1; 16311df65c3SArjan van de Ven } 1641f8cdae4SHui Zhu if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) { 165d32ad102SArjan van de Ven $function = $1; 166d32ad102SArjan van de Ven $func_offset = $2; 1675aea50b5SArjan van de Ven } 168ef2b9b05SHui Zhu if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\] \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) { 16911df65c3SArjan van de Ven $function = $1; 17011df65c3SArjan van de Ven $func_offset = $2; 17111df65c3SArjan van de Ven } 1725aea50b5SArjan van de Ven 173d32ad102SArjan van de Ven # check if it's a module 174d32ad102SArjan 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\_\-]+)\]/) { 175d32ad102SArjan van de Ven $module = $3; 176d32ad102SArjan van de Ven } 17711df65c3SArjan van de Ven 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\_\-]+)\]/) { 17811df65c3SArjan van de Ven $module = $3; 17911df65c3SArjan van de Ven } 180c19ef7fdSArjan van de Ven parse_x86_regs($line); 1815aea50b5SArjan van de Ven} 1825aea50b5SArjan van de Ven 18351fbb4baSMatthew Wilcoxmy $decodestart = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$func_offset"); 18451fbb4baSMatthew Wilcoxmy $decodestop = Math::BigInt->from_hex("0x$target") + 8192; 1855aea50b5SArjan van de Venif ($target eq "0") { 1865aea50b5SArjan van de Ven print "No oops found!\n"; 18752e13e21SHui Zhu usage(); 1885aea50b5SArjan van de Ven} 1895aea50b5SArjan van de Ven 190d32ad102SArjan van de Ven# if it's a module, we need to find the .ko file and calculate a load offset 191d32ad102SArjan van de Venif ($module ne "") { 19252e13e21SHui Zhu if ($modulefile eq "") { 19359dde385SHui Zhu $modulefile = `modinfo -F filename $module`; 194d32ad102SArjan van de Ven chomp($modulefile); 19552e13e21SHui Zhu } 196d32ad102SArjan van de Ven $filename = $modulefile; 197d32ad102SArjan van de Ven if ($filename eq "") { 198d32ad102SArjan van de Ven print "Module .ko file for $module not found. Aborting\n"; 199d32ad102SArjan van de Ven exit; 200d32ad102SArjan van de Ven } 201d32ad102SArjan van de Ven # ok so we found the module, now we need to calculate the vma offset 20252e13e21SHui Zhu open(FILE, $cross_compile."objdump -dS $filename |") || die "Cannot start objdump"; 203d32ad102SArjan van de Ven while (<FILE>) { 204d32ad102SArjan van de Ven if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) { 205d32ad102SArjan van de Ven my $fu = $1; 20652e13e21SHui Zhu $vmaoffset = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$fu") - Math::BigInt->from_hex("0x$func_offset"); 207d32ad102SArjan van de Ven } 208d32ad102SArjan van de Ven } 209d32ad102SArjan van de Ven close(FILE); 210d32ad102SArjan van de Ven} 211d32ad102SArjan van de Ven 2125aea50b5SArjan van de Venmy $counter = 0; 2135aea50b5SArjan van de Venmy $state = 0; 2140139f1d9SHui Zhumy $center = -1; 2155aea50b5SArjan van de Venmy @lines; 216c19ef7fdSArjan van de Venmy @reglines; 2175aea50b5SArjan van de Ven 2185aea50b5SArjan van de Vensub InRange { 2195aea50b5SArjan van de Ven my ($address, $target) = @_; 2205aea50b5SArjan van de Ven my $ad = "0x".$address; 2215aea50b5SArjan van de Ven my $ta = "0x".$target; 22252e13e21SHui Zhu my $delta = Math::BigInt->from_hex($ad) - Math::BigInt->from_hex($ta); 2235aea50b5SArjan van de Ven 2245aea50b5SArjan van de Ven if (($delta > -4096) && ($delta < 4096)) { 2255aea50b5SArjan van de Ven return 1; 2265aea50b5SArjan van de Ven } 2275aea50b5SArjan van de Ven return 0; 2285aea50b5SArjan van de Ven} 2295aea50b5SArjan van de Ven 2305aea50b5SArjan van de Ven 2315aea50b5SArjan van de Ven 2325aea50b5SArjan van de Ven# first, parse the input into the lines array, but to keep size down, 2335aea50b5SArjan van de Ven# we only do this for 4Kb around the sweet spot 2345aea50b5SArjan van de Ven 23552e13e21SHui Zhuopen(FILE, $cross_compile."objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump"; 2365aea50b5SArjan van de Ven 2375aea50b5SArjan van de Venwhile (<FILE>) { 2385aea50b5SArjan van de Ven my $line = $_; 2395aea50b5SArjan van de Ven chomp($line); 2405aea50b5SArjan van de Ven if ($state == 0) { 2415aea50b5SArjan van de Ven if ($line =~ /^([a-f0-9]+)\:/) { 2425aea50b5SArjan van de Ven if (InRange($1, $target)) { 2435aea50b5SArjan van de Ven $state = 1; 2445aea50b5SArjan van de Ven } 2455aea50b5SArjan van de Ven } 2460139f1d9SHui Zhu } 2470139f1d9SHui Zhu if ($state == 1) { 2485aea50b5SArjan van de Ven if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) { 2495aea50b5SArjan van de Ven my $val = $1; 2505aea50b5SArjan van de Ven if (!InRange($val, $target)) { 2515aea50b5SArjan van de Ven last; 2525aea50b5SArjan van de Ven } 2535aea50b5SArjan van de Ven if ($val eq $target) { 2545aea50b5SArjan van de Ven $center = $counter; 2555aea50b5SArjan van de Ven } 2565aea50b5SArjan van de Ven } 2575aea50b5SArjan van de Ven $lines[$counter] = $line; 2585aea50b5SArjan van de Ven 2595aea50b5SArjan van de Ven $counter = $counter + 1; 2605aea50b5SArjan van de Ven } 2615aea50b5SArjan van de Ven} 2625aea50b5SArjan van de Ven 2635aea50b5SArjan van de Venclose(FILE); 2645aea50b5SArjan van de Ven 2655aea50b5SArjan van de Venif ($counter == 0) { 2665aea50b5SArjan van de Ven print "No matching code found \n"; 2675aea50b5SArjan van de Ven exit; 2685aea50b5SArjan van de Ven} 2695aea50b5SArjan van de Ven 2700139f1d9SHui Zhuif ($center == -1) { 2715aea50b5SArjan van de Ven print "No matching code found \n"; 2725aea50b5SArjan van de Ven exit; 2735aea50b5SArjan van de Ven} 2745aea50b5SArjan van de Ven 2755aea50b5SArjan van de Venmy $start; 2765aea50b5SArjan van de Venmy $finish; 2775aea50b5SArjan van de Venmy $codelines = 0; 2785aea50b5SArjan van de Venmy $binarylines = 0; 2795aea50b5SArjan van de Ven# now we go up and down in the array to find how much we want to print 2805aea50b5SArjan van de Ven 2815aea50b5SArjan van de Ven$start = $center; 2825aea50b5SArjan van de Ven 2835aea50b5SArjan van de Venwhile ($start > 1) { 2845aea50b5SArjan van de Ven $start = $start - 1; 2855aea50b5SArjan van de Ven my $line = $lines[$start]; 2865aea50b5SArjan van de Ven if ($line =~ /^([a-f0-9]+)\:/) { 2875aea50b5SArjan van de Ven $binarylines = $binarylines + 1; 2885aea50b5SArjan van de Ven } else { 2895aea50b5SArjan van de Ven $codelines = $codelines + 1; 2905aea50b5SArjan van de Ven } 2915aea50b5SArjan van de Ven if ($codelines > 10) { 2925aea50b5SArjan van de Ven last; 2935aea50b5SArjan van de Ven } 2945aea50b5SArjan van de Ven if ($binarylines > 20) { 2955aea50b5SArjan van de Ven last; 2965aea50b5SArjan van de Ven } 2975aea50b5SArjan van de Ven} 2985aea50b5SArjan van de Ven 2995aea50b5SArjan van de Ven 3005aea50b5SArjan van de Ven$finish = $center; 3015aea50b5SArjan van de Ven$codelines = 0; 3025aea50b5SArjan van de Ven$binarylines = 0; 3035aea50b5SArjan van de Venwhile ($finish < $counter) { 3045aea50b5SArjan van de Ven $finish = $finish + 1; 3055aea50b5SArjan van de Ven my $line = $lines[$finish]; 3065aea50b5SArjan van de Ven if ($line =~ /^([a-f0-9]+)\:/) { 3075aea50b5SArjan van de Ven $binarylines = $binarylines + 1; 3085aea50b5SArjan van de Ven } else { 3095aea50b5SArjan van de Ven $codelines = $codelines + 1; 3105aea50b5SArjan van de Ven } 3115aea50b5SArjan van de Ven if ($codelines > 10) { 3125aea50b5SArjan van de Ven last; 3135aea50b5SArjan van de Ven } 3145aea50b5SArjan van de Ven if ($binarylines > 20) { 3155aea50b5SArjan van de Ven last; 3165aea50b5SArjan van de Ven } 3175aea50b5SArjan van de Ven} 3185aea50b5SArjan van de Ven 3195aea50b5SArjan van de Ven 3205aea50b5SArjan van de Venmy $i; 3215aea50b5SArjan van de Ven 322c19ef7fdSArjan van de Ven 323c19ef7fdSArjan van de Ven# start annotating the registers in the asm. 324c19ef7fdSArjan van de Ven# this goes from the oopsing point back, so that the annotator 325c19ef7fdSArjan van de Ven# can track (opportunistically) which registers got written and 326c19ef7fdSArjan van de Ven# whos value no longer is relevant. 327c19ef7fdSArjan van de Ven 328c19ef7fdSArjan van de Ven$i = $center; 329c19ef7fdSArjan van de Venwhile ($i >= $start) { 330c19ef7fdSArjan van de Ven $reglines[$i] = process_x86_regs($lines[$i], $center - $i); 331c19ef7fdSArjan van de Ven $i = $i - 1; 3325aea50b5SArjan van de Ven} 3335aea50b5SArjan van de Ven 334c19ef7fdSArjan van de Ven$i = $start; 335c19ef7fdSArjan van de Venwhile ($i < $finish) { 336c19ef7fdSArjan van de Ven my $line; 337c19ef7fdSArjan van de Ven if ($i == $center) { 338c19ef7fdSArjan van de Ven $line = "*$lines[$i] "; 339c19ef7fdSArjan van de Ven } else { 340c19ef7fdSArjan van de Ven $line = " $lines[$i] "; 341c19ef7fdSArjan van de Ven } 342c19ef7fdSArjan van de Ven print $line; 343c19ef7fdSArjan van de Ven if (defined($reglines[$i]) && length($reglines[$i]) > 0) { 344c19ef7fdSArjan van de Ven my $c = 60 - length($line); 345c19ef7fdSArjan van de Ven while ($c > 0) { print " "; $c = $c - 1; }; 346c19ef7fdSArjan van de Ven print "| $reglines[$i]"; 347c19ef7fdSArjan van de Ven } 348c19ef7fdSArjan van de Ven if ($i == $center) { 349c19ef7fdSArjan van de Ven print "<--- faulting instruction"; 350c19ef7fdSArjan van de Ven } 351c19ef7fdSArjan van de Ven print "\n"; 352c19ef7fdSArjan van de Ven $i = $i +1; 353c19ef7fdSArjan van de Ven} 3545aea50b5SArjan van de Ven 35552e13e21SHui Zhusub usage { 35652e13e21SHui Zhu print <<EOT; 35752e13e21SHui ZhuUsage: 35852e13e21SHui Zhu dmesg | perl $0 [OPTION] [VMLINUX] 35952e13e21SHui Zhu 36052e13e21SHui ZhuOPTION: 36152e13e21SHui Zhu -c, --cross-compile CROSS_COMPILE Specify the prefix used for toolchain. 36259dde385SHui Zhu -m, --module MODULE_DIRNAME Specify the module filename. 36352e13e21SHui Zhu -h, --help Help. 36452e13e21SHui ZhuEOT 36552e13e21SHui Zhu exit; 36652e13e21SHui Zhu} 367