Blame view

scripts/markup_oops.pl 7.51 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;
5aea50b5c   Arjan van de Ven   scripts: script f...
4
5
6
7
8
9
10
11
12
13
14
15
16
  # 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...
17
18
19
20
21
22
23
24
  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...
25
26
27
28
29
  #
  # Step 1: Parse the oops to find the EIP value
  #
  
  my $target = "0";
d32ad102c   Arjan van de Ven   script: improve m...
30
31
  my $function;
  my $module = "";
11df65c3c   Arjan van de Ven   scripts: add x86 ...
32
  my $func_offset = 0;
d32ad102c   Arjan van de Ven   script: improve m...
33
  my $vmaoffset = 0;
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  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 ...
51
52
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
  	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 ...
84
85
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
  }
  
  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 ...
118
119
  		my $clobberprime = reg_name($clobber);
  		my $lastwordprime = reg_name($lastword);
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
120
  		my $val = $regs{$reg};
11df65c3c   Arjan van de Ven   scripts: add x86 ...
121
122
123
124
125
  		if ($val =~ /^[0]+$/) {
  			$val = "0";
  		} else {
  			$val =~ s/^0*//;
  		}
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
126
127
  		# 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 ...
128
  		if ($clobber =~ /$reg/ || $clobberprime =~ /$reg/) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
129
130
131
132
133
134
135
  			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 ...
136
  		if ($lastword =~ /$reg/ || $lastwordprime =~ /$reg/) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
137
138
139
140
141
142
143
144
145
  			if (length($val) > 0) {
  				$str = $str . " $reg = $val ";
  			}
  		}
  	}
  	return $str;
  }
  
  # parse the oops
5aea50b5c   Arjan van de Ven   scripts: script f...
146
  while (<STDIN>) {
d32ad102c   Arjan van de Ven   script: improve m...
147
148
  	my $line = $_;
  	if ($line =~ /EIP: 0060:\[\<([a-z0-9]+)\>\]/) {
5aea50b5c   Arjan van de Ven   scripts: script f...
149
150
  		$target = $1;
  	}
11df65c3c   Arjan van de Ven   scripts: add x86 ...
151
152
153
  	if ($line =~ /RIP: 0010:\[\<([a-z0-9]+)\>\]/) {
  		$target = $1;
  	}
d32ad102c   Arjan van de Ven   script: improve m...
154
155
156
157
  	if ($line =~ /EIP is at ([a-zA-Z0-9\_]+)\+(0x[0-9a-f]+)\/0x[a-f0-9]/) {
  		$function = $1;
  		$func_offset = $2;
  	}
11df65c3c   Arjan van de Ven   scripts: add x86 ...
158
159
160
161
  	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;
  	}
5aea50b5c   Arjan van de Ven   scripts: script f...
162

d32ad102c   Arjan van de Ven   script: improve m...
163
164
165
166
  	# 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 ...
167
168
169
  	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 ...
170
  	parse_x86_regs($line);
5aea50b5c   Arjan van de Ven   scripts: script f...
171
  }
d32ad102c   Arjan van de Ven   script: improve m...
172
  my $decodestart = hex($target) - hex($func_offset);
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
173
  my $decodestop = hex($target) + 8192;
5aea50b5c   Arjan van de Ven   scripts: script f...
174
175
176
177
178
179
180
181
182
  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...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  # if it's a module, we need to find the .ko file and calculate a load offset
  if ($module ne "") {
  	my $dir = dirname($filename);
  	$dir = $dir . "/";
  	my $mod = $module . ".ko";
  	my $modulefile = `find $dir -name $mod | head -1`;
  	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...
206
207
208
209
  my $counter = 0;
  my $state   = 0;
  my $center  = 0;
  my @lines;
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
210
  my @reglines;
5aea50b5c   Arjan van de Ven   scripts: script f...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  
  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...
228
  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...
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
314
  
  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 ...
315
316
317
318
319
320
321
322
323
324
325
  
  # 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...
326
327
  $i = $start;
  while ($i < $finish) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
328
  	my $line;
5aea50b5c   Arjan van de Ven   scripts: script f...
329
  	if ($i == $center) {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
330
  		$line =  "*$lines[$i] ";
5aea50b5c   Arjan van de Ven   scripts: script f...
331
  	} else {
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
332
333
334
335
336
337
338
  		$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...
339
  	}
c19ef7fd8   Arjan van de Ven   scripts: add x86 ...
340
341
342
343
344
  	if ($i == $center) {
  		print "<--- faulting instruction";
  	}
  	print "
  ";
5aea50b5c   Arjan van de Ven   scripts: script f...
345
346
  	$i = $i +1;
  }