markup_oops.pl 4.0 KB

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