script: improve markup_oops.pl to also decode oopses in modules
[safe/jmp/linux-2.6] / scripts / markup_oops.pl
1 #!/usr/bin/perl -w
2
3 use File::Basename;
4
5 # Copyright 2008, Intel Corporation
6 #
7 # This file is part of the Linux kernel
8 #
9 # This program file is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as published by the
11 # Free Software Foundation; version 2 of the License.
12 #
13 # Authors:
14 #       Arjan van de Ven <arjan@linux.intel.com>
15
16
17 my $vmlinux_name = $ARGV[0];
18 if (!defined($vmlinux_name)) {
19         my $kerver = `uname -r`;
20         chomp($kerver);
21         $vmlinux_name = "/lib/modules/$kerver/build/vmlinux";
22         print "No vmlinux specified, assuming $vmlinux_name\n";
23 }
24 my $filename = $vmlinux_name;
25 #
26 # Step 1: Parse the oops to find the EIP value
27 #
28
29 my $target = "0";
30 my $function;
31 my $module = "";
32 my $func_offset;
33 my $vmaoffset = 0;
34
35 while (<STDIN>) {
36         my $line = $_;
37         if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
38                 $target = $1;
39         }
40         if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]/) {
41                 $function = $1;
42                 $func_offset = $2;
43         }
44
45         # check if it's a module
46         if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]+\W\[([a-zA-Z0-9\_\-]+)\]/) {
47                 $module = $3;
48         }
49 }
50
51 my $decodestart = hex($target) - hex($func_offset);
52 my $decodestop = $decodestart + 8192;
53 if ($target eq "0") {
54         print "No oops found!\n";
55         print "Usage: \n";
56         print "    dmesg | perl scripts/markup_oops.pl vmlinux\n";
57         exit;
58 }
59
60 # if it's a module, we need to find the .ko file and calculate a load offset
61 if ($module ne "") {
62         my $dir = dirname($filename);
63         $dir = $dir . "/";
64         my $mod = $module . ".ko";
65         my $modulefile = `find $dir -name $mod | head -1`;
66         chomp($modulefile);
67         $filename = $modulefile;
68         if ($filename eq "") {
69                 print "Module .ko file for $module not found. Aborting\n";
70                 exit;
71         }
72         # ok so we found the module, now we need to calculate the vma offset
73         open(FILE, "objdump -dS $filename |") || die "Cannot start objdump";
74         while (<FILE>) {
75                 if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) {
76                         my $fu = $1;
77                         $vmaoffset = hex($target) - hex($fu) - hex($func_offset);
78                 }
79         }
80         close(FILE);
81 }
82
83 my $counter = 0;
84 my $state   = 0;
85 my $center  = 0;
86 my @lines;
87
88 sub InRange {
89         my ($address, $target) = @_;
90         my $ad = "0x".$address;
91         my $ta = "0x".$target;
92         my $delta = hex($ad) - hex($ta);
93
94         if (($delta > -4096) && ($delta < 4096)) {
95                 return 1;
96         }
97         return 0;
98 }
99
100
101
102 # first, parse the input into the lines array, but to keep size down,
103 # we only do this for 4Kb around the sweet spot
104
105 open(FILE, "objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump";
106
107 while (<FILE>) {
108         my $line = $_;
109         chomp($line);
110         if ($state == 0) {
111                 if ($line =~ /^([a-f0-9]+)\:/) {
112                         if (InRange($1, $target)) {
113                                 $state = 1;
114                         }
115                 }
116         } else {
117                 if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) {
118                         my $val = $1;
119                         if (!InRange($val, $target)) {
120                                 last;
121                         }
122                         if ($val eq $target) {
123                                 $center = $counter;
124                         }
125                 }
126                 $lines[$counter] = $line;
127
128                 $counter = $counter + 1;
129         }
130 }
131
132 close(FILE);
133
134 if ($counter == 0) {
135         print "No matching code found \n";
136         exit;
137 }
138
139 if ($center == 0) {
140         print "No matching code found \n";
141         exit;
142 }
143
144 my $start;
145 my $finish;
146 my $codelines = 0;
147 my $binarylines = 0;
148 # now we go up and down in the array to find how much we want to print
149
150 $start = $center;
151
152 while ($start > 1) {
153         $start = $start - 1;
154         my $line = $lines[$start];
155         if ($line =~ /^([a-f0-9]+)\:/) {
156                 $binarylines = $binarylines + 1;
157         } else {
158                 $codelines = $codelines + 1;
159         }
160         if ($codelines > 10) {
161                 last;
162         }
163         if ($binarylines > 20) {
164                 last;
165         }
166 }
167
168
169 $finish = $center;
170 $codelines = 0;
171 $binarylines = 0;
172 while ($finish < $counter) {
173         $finish = $finish + 1;
174         my $line = $lines[$finish];
175         if ($line =~ /^([a-f0-9]+)\:/) {
176                 $binarylines = $binarylines + 1;
177         } else {
178                 $codelines = $codelines + 1;
179         }
180         if ($codelines > 10) {
181                 last;
182         }
183         if ($binarylines > 20) {
184                 last;
185         }
186 }
187
188
189 my $i;
190
191 my $fulltext = "";
192 $i = $start;
193 while ($i < $finish) {
194         if ($i == $center) {
195                 $fulltext = $fulltext . "*$lines[$i]     <----- faulting instruction\n";
196         } else {
197                 $fulltext = $fulltext .  " $lines[$i]\n";
198         }
199         $i = $i +1;
200 }
201
202 print $fulltext;
203