2009-01-13 15:00:46

by Arjan van de Ven

[permalink] [raw]
Subject: [patch 0/2] Improve the markup_oops.pl script

Hi,

the following 2 patches enhance the markup_oops.pl script in two ways
Patch 1: Add support for putting register values in the asm dump
Patch 2: Add support for 64 bit X86

The output (on a 32 bit OS with a "make me oops" example module) looks like
the pasted dump; for the instruction where the oops happened, as well as
a few instructions before that where the dumper can determine the register value.
the value is appended on said line.


if (bar-1) {
f8436012: 8b 45 fc mov -0x4(%ebp),%eax
f8436015: 48 dec %eax
f8436016: 74 09 je f8436021 <foo_bar+0x21>
bar = bar * 5;
f8436018: 8b 45 fc mov -0x4(%ebp),%eax
f843601b: 8d 04 80 lea (%eax,%eax,4),%eax | %eax => 0
f843601e: 89 45 fc mov %eax,-0x4(%ebp) | %eax = 0
}
*foo = 1;
*f8436021: c6 02 01 movb $0x1,(%edx) | %edx = 0 <--- faulting instruction
return bar;
f8436024: 8b 45 fc mov -0x4(%ebp),%eax
}
f8436027: c9 leave
f8436028: c3 ret

--
Arjan van de Ven Intel Open Source Technology Centre
For development, discussion and tips for power savings,
visit http://www.lesswatts.org


2009-01-13 15:01:13

by Arjan van de Ven

[permalink] [raw]
Subject: [PATCH 1/2] scripts: add x86 register parser to markup_oops.pl

>From ace27f35478ed449fd345d2381411362f6bcc9f9 Mon Sep 17 00:00:00 2001
From: Arjan van de Ven <[email protected]>
Date: Tue, 13 Jan 2009 05:50:35 +0000
Subject: [PATCH] scripts: add x86 register parser to markup_oops.pl

An oops dump also contains the register values.
This patch parses these for (32 bit) x86, and then annotates the
disassembly with these values; this helps in analysis of the oops
by the developer, for example, NULL pointer or other pointer bugs
show up clearly this way.

Signed-off-by: Arjan van de Ven <[email protected]>
---
scripts/markup_oops.pl | 106 +++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 100 insertions(+), 6 deletions(-)

diff --git a/scripts/markup_oops.pl b/scripts/markup_oops.pl
index d40449c..bb20468 100644
--- a/scripts/markup_oops.pl
+++ b/scripts/markup_oops.pl
@@ -32,6 +32,78 @@ my $module = "";
my $func_offset;
my $vmaoffset = 0;

+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;
+ }
+}
+
+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)) {
+ my $val = $regs{$reg};
+ # first check if we're clobbering this register; if we do
+ # we print it with a =>, and then delete its value
+ if ($clobber =~ /$reg/) {
+ if (length($val) > 0) {
+ $str = $str . " $reg => $val ";
+ }
+ $regs{$reg} = "";
+ $val = "";
+ }
+ # now check if we're reading this register
+ if ($lastword =~ /$reg/) {
+ if (length($val) > 0) {
+ $str = $str . " $reg = $val ";
+ }
+ }
+ }
+ return $str;
+}
+
+# parse the oops
while (<STDIN>) {
my $line = $_;
if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
@@ -46,10 +118,11 @@ while (<STDIN>) {
if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
$module = $3;
}
+ parse_x86_regs($line);
}

my $decodestart = hex($target) - hex($func_offset);
-my $decodestop = $decodestart + 8192;
+my $decodestop = hex($target) + 8192;
if ($target eq "0") {
print "No oops found!\n";
print "Usage: \n";
@@ -84,6 +157,7 @@ my $counter = 0;
my $state = 0;
my $center = 0;
my @lines;
+my @reglines;

sub InRange {
my ($address, $target) = @_;
@@ -188,16 +262,36 @@ while ($finish < $counter) {

my $i;

-my $fulltext = "";
+
+# 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;
+}
+
$i = $start;
while ($i < $finish) {
+ my $line;
if ($i == $center) {
- $fulltext = $fulltext . "*$lines[$i] <----- faulting instruction\n";
+ $line = "*$lines[$i] ";
} else {
- $fulltext = $fulltext . " $lines[$i]\n";
+ $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]";
}
+ if ($i == $center) {
+ print "<--- faulting instruction";
+ }
+ print "\n";
$i = $i +1;
}

-print $fulltext;
-
--
1.6.0.6




--
Arjan van de Ven Intel Open Source Technology Centre
For development, discussion and tips for power savings,
visit http://www.lesswatts.org

2009-01-13 15:01:30

by Arjan van de Ven

[permalink] [raw]
Subject: [PATCH 2/2] scripts: add x86 64 bit support to the markup_oops.pl script

>From 231381ec24bf8f13fb87a06ae937032842a67225 Mon Sep 17 00:00:00 2001
From: Arjan van de Ven <[email protected]>
Date: Tue, 13 Jan 2009 09:45:53 +0000
Subject: [PATCH] scripts: add x86 64 bit support to the markup_oops.pl script

---
scripts/markup_oops.pl | 59 ++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/scripts/markup_oops.pl b/scripts/markup_oops.pl
index bb20468..9b90b6c 100644
--- a/scripts/markup_oops.pl
+++ b/scripts/markup_oops.pl
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl

use File::Basename;

@@ -29,7 +29,7 @@ my $filename = $vmlinux_name;
my $target = "0";
my $function;
my $module = "";
-my $func_offset;
+my $func_offset = 0;
my $vmaoffset = 0;

my %regs;
@@ -49,6 +49,39 @@ sub parse_x86_regs
$regs{"%edi"} = $2;
$regs{"%esp"} = $4;
}
+ 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;
}

sub process_x86_regs
@@ -83,10 +116,18 @@ sub process_x86_regs
}

foreach $reg (keys(%regs)) {
+ my $clobberprime = reg_name($clobber);
+ my $lastwordprime = reg_name($lastword);
my $val = $regs{$reg};
+ if ($val =~ /^[0]+$/) {
+ $val = "0";
+ } else {
+ $val =~ s/^0*//;
+ }
+
# first check if we're clobbering this register; if we do
# we print it with a =>, and then delete its value
- if ($clobber =~ /$reg/) {
+ if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) {
if (length($val) > 0) {
$str = $str . " $reg => $val ";
}
@@ -94,7 +135,7 @@ sub process_x86_regs
$val = "";
}
# now check if we're reading this register
- if ($lastword =~ /$reg/) {
+ if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) {
if (length($val) > 0) {
$str = $str . " $reg = $val ";
}
@@ -109,15 +150,25 @@ while (<STDIN>) {
if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
$target = $1;
}
+ if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) {
+ $target = $1;
+ }
if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]/) {
$function = $1;
$func_offset = $2;
}
+ if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\] \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]/) {
+ $function = $1;
+ $func_offset = $2;
+ }

# 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;
}
+ 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;
+ }
parse_x86_regs($line);
}

--
1.6.0.6




--
Arjan van de Ven Intel Open Source Technology Centre
For development, discussion and tips for power savings,
visit http://www.lesswatts.org