Blame view

scripts/markup_oops.pl 7.54 KB
11df65c3c   Arjan van de Ven   scripts: add x86 ...
1
  #!/usr/bin/perl
5aea50b5c   Arjan van de Ven   scripts: script f...
2

d32ad102c   Arjan van de Ven   script: improve m...
3
  use File::Basename;
51fbb4bab   Matthew Wilcox   markup_oops: fix ...
4
  use Math::BigInt;
d32ad102c   Arjan van de Ven   script: improve m...
5

5aea50b5c   Arjan van de Ven   scripts: script f...
6
7
8
9
10
11
12
13
14
15
16
17
18
  # Copyright 2008, Intel Corporation
  #
  # This file is part of the Linux kernel
  #
  # This program file is free software; you can redistribute it and/or modify it
  # under the terms of the GNU General Public License as published by the
  # Free Software Foundation; version 2 of the License.
  #
  # Authors:
  # 	Arjan van de Ven <arjan@linux.intel.com>
  
  
  my $vmlinux_name = $ARGV[0];
d32ad102c   Arjan van de Ven   script: improve m...
19
20
21
22
23
24
25
26
  if (!defined($vmlinux_name)) {
  	my $kerver = `uname -r`;
  	chomp($kerver);
  	$vmlinux_name = "/lib/modules/$kerver/build/vmlinux";
  	print "No vmlinux specified, assuming $vmlinux_name
  ";
  }
  my $filename = $vmlinux_name;
5aea50b5c   Arjan van de Ven   scripts: script f...
27
28
29
30
31
  #
  # Step 1: Parse the oops to find the EIP value
  #
  
  my $target = "0";
d32ad102c   Arjan van de Ven   script: improve m...
32
33
  my $function;
  my $module = "";
11df65c3c   Arjan van de Ven   scripts: add x86 ...
34
  my $func_offset = 0;
d32ad102c   Arjan van de Ven   script: improve m...
35
  my $vmaoffset = 0;
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  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;
  	}
11df65c3c   Arjan van de Ven   scripts: add x86 ...
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  	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;
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  }
  
  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)) {
11df65c3c   Arjan van de Ven   scripts: add x86 ...
120
121
  		my $clobberprime = reg_name($clobber);
  		my $lastwordprime = reg_name($lastword);
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
122
  		my $val = $regs{$reg};
11df65c3c   Arjan van de Ven   scripts: add x86 ...
123
124
125
126
127
  		if ($val =~ /^[0]+$/) {
  			$val = "0";
  		} else {
  			$val =~ s/^0*//;
  		}
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
128
129
  		# first check if we're clobbering this register; if we do
  		# we print it with a =>, and then delete its value
11df65c3c   Arjan van de Ven   scripts: add x86 ...
130
  		if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
131
132
133
134
135
136
137
  			if (length($val) > 0) {
  				$str = $str . " $reg => $val ";
  			}
  			$regs{$reg} = "";
  			$val = "";
  		}
  		# now check if we're reading this register
11df65c3c   Arjan van de Ven   scripts: add x86 ...
138
  		if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
139
140
141
142
143
144
145
146
147
  			if (length($val) > 0) {
  				$str = $str . " $reg = $val ";
  			}
  		}
  	}
  	return $str;
  }
  
  # parse the oops
5aea50b5c   Arjan van de Ven   scripts: script f...
148
  while (<STDIN>) {
d32ad102c   Arjan van de Ven   script: improve m...
149
150
  	my $line = $_;
  	if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
5aea50b5c   Arjan van de Ven   scripts: script f...
151
152
  		$target = $1;
  	}
11df65c3c   Arjan van de Ven   scripts: add x86 ...
153
154
155
  	if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) {
  		$target = $1;
  	}
1f8cdae43   Hui Zhu   markup_oops.pl: f...
156
  	if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) {
d32ad102c   Arjan van de Ven   script: improve m...
157
158
159
  		$function = $1;
  		$func_offset = $2;
  	}
ef2b9b054   Hui Zhu   markup_oops.pl: f...
160
  	if ($line =~ /RIP: 0010:\[\<[0-9a-f]+\>\]  \[\<[0-9a-f]+\>\] ([a-zA-Z0-9\_]+)\+0x([0-9a-f]+)\/0x[a-f0-9]/) {
11df65c3c   Arjan van de Ven   scripts: add x86 ...
161
162
163
  		$function = $1;
  		$func_offset = $2;
  	}
5aea50b5c   Arjan van de Ven   scripts: script f...
164

d32ad102c   Arjan van de Ven   script: improve m...
165
166
167
168
  	# 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;
  	}
11df65c3c   Arjan van de Ven   scripts: add x86 ...
169
170
171
  	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;
  	}
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
172
  	parse_x86_regs($line);
5aea50b5c   Arjan van de Ven   scripts: script f...
173
  }
51fbb4bab   Matthew Wilcox   markup_oops: fix ...
174
175
  my $decodestart = Math::BigInt->from_hex("0x$target") - Math::BigInt->from_hex("0x$func_offset");
  my $decodestop = Math::BigInt->from_hex("0x$target") + 8192;
5aea50b5c   Arjan van de Ven   scripts: script f...
176
177
178
179
180
181
182
183
184
  if ($target eq "0") {
  	print "No oops found!
  ";
  	print "Usage: 
  ";
  	print "    dmesg | perl scripts/markup_oops.pl vmlinux
  ";
  	exit;
  }
d32ad102c   Arjan van de Ven   script: improve m...
185
186
  # if it's a module, we need to find the .ko file and calculate a load offset
  if ($module ne "") {
82fa39552   Ozan Çaglayan   markup_oops: use ...
187
  	my $modulefile = `modinfo $module | grep '^filename:' | awk '{ print \$2 }'`;
d32ad102c   Arjan van de Ven   script: improve m...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  	chomp($modulefile);
  	$filename = $modulefile;
  	if ($filename eq "") {
  		print "Module .ko file for $module not found. Aborting
  ";
  		exit;
  	}
  	# ok so we found the module, now we need to calculate the vma offset
  	open(FILE, "objdump -dS $filename |") || die "Cannot start objdump";
  	while (<FILE>) {
  		if ($_ =~ /^([0-9a-f]+) \<$function\>\:/) {
  			my $fu = $1;
  			$vmaoffset = hex($target) - hex($fu) - hex($func_offset);
  		}
  	}
  	close(FILE);
  }
5aea50b5c   Arjan van de Ven   scripts: script f...
205
206
207
208
  my $counter = 0;
  my $state   = 0;
  my $center  = 0;
  my @lines;
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
209
  my @reglines;
5aea50b5c   Arjan van de Ven   scripts: script f...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  
  sub InRange {
  	my ($address, $target) = @_;
  	my $ad = "0x".$address;
  	my $ta = "0x".$target;
  	my $delta = hex($ad) - hex($ta);
  
  	if (($delta > -4096) && ($delta < 4096)) {
  		return 1;
  	}
  	return 0;
  }
  
  
  
  # first, parse the input into the lines array, but to keep size down,
  # we only do this for 4Kb around the sweet spot
d32ad102c   Arjan van de Ven   script: improve m...
227
  open(FILE, "objdump -dS --adjust-vma=$vmaoffset --start-address=$decodestart --stop-address=$decodestop $filename |") || die "Cannot start objdump";
5aea50b5c   Arjan van de Ven   scripts: script f...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  
  while (<FILE>) {
  	my $line = $_;
  	chomp($line);
  	if ($state == 0) {
  		if ($line =~ /^([a-f0-9]+)\:/) {
  			if (InRange($1, $target)) {
  				$state = 1;
  			}
  		}
  	} else {
  		if ($line =~ /^([a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]+)\:/) {
  			my $val = $1;
  			if (!InRange($val, $target)) {
  				last;
  			}
  			if ($val eq $target) {
  				$center = $counter;
  			}
  		}
  		$lines[$counter] = $line;
  
  		$counter = $counter + 1;
  	}
  }
  
  close(FILE);
  
  if ($counter == 0) {
  	print "No matching code found 
  ";
  	exit;
  }
  
  if ($center == 0) {
  	print "No matching code found 
  ";
  	exit;
  }
  
  my $start;
  my $finish;
  my $codelines = 0;
  my $binarylines = 0;
  # now we go up and down in the array to find how much we want to print
  
  $start = $center;
  
  while ($start > 1) {
  	$start = $start - 1;
  	my $line = $lines[$start];
  	if ($line =~ /^([a-f0-9]+)\:/) {
  		$binarylines = $binarylines + 1;
  	} else {
  		$codelines = $codelines + 1;
  	}
  	if ($codelines > 10) {
  		last;
  	}
  	if ($binarylines > 20) {
  		last;
  	}
  }
  
  
  $finish = $center;
  $codelines = 0;
  $binarylines = 0;
  while ($finish < $counter) {
  	$finish = $finish + 1;
  	my $line = $lines[$finish];
  	if ($line =~ /^([a-f0-9]+)\:/) {
  		$binarylines = $binarylines + 1;
  	} else {
  		$codelines = $codelines + 1;
  	}
  	if ($codelines > 10) {
  		last;
  	}
  	if ($binarylines > 20) {
  		last;
  	}
  }
  
  
  my $i;
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
314
315
316
317
318
319
320
321
322
323
324
  
  # 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;
  }
5aea50b5c   Arjan van de Ven   scripts: script f...
325
326
  $i = $start;
  while ($i < $finish) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
327
  	my $line;
5aea50b5c   Arjan van de Ven   scripts: script f...
328
  	if ($i == $center) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
329
  		$line =  "*$lines[$i] ";
5aea50b5c   Arjan van de Ven   scripts: script f...
330
  	} else {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
331
332
333
334
335
336
337
  		$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]";
5aea50b5c   Arjan van de Ven   scripts: script f...
338
  	}
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
339
340
341
342
343
  	if ($i == $center) {
  		print "<--- faulting instruction";
  	}
  	print "
  ";
5aea50b5c   Arjan van de Ven   scripts: script f...
344
345
  	$i = $i +1;
  }