root/scripts/ksymoops.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. strequ
  2. strnequ
  3. set_extent
  4. scan
  5. print
  6. valid
  7. find
  8. decode
  9. scan
  10. usage
  11. main

   1 // ksymoops.cc v1.7 -- A simple filter to resolve symbols in Linux Oops-logs
   2 // Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
   3 // compile like so: g++ -o ksymoops ksymoops.cc -liostream
   4 
   5 //////////////////////////////////////////////////////////////////////////////
   6 
   7 // This program is free software; you can redistribute it and/or modify
   8 // it under the terms of the GNU General Public License as published by
   9 // the Free Software Foundation; either version 2, or (at your option)
  10 // any later version.
  11 
  12 // This program is distributed in the hope that it will be useful,
  13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 // GNU General Public License for more details.
  16 
  17 // You should have received a copy of the GNU General Public License
  18 // along with this program; see the file COPYING.  If not, write to the
  19 // Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 
  21 // This is a simple filter to resolve EIP and call-trace symbols from
  22 // a Linux kernel "Oops" log.  Supply the symbol-map file name as a
  23 // command-line argument, and redirect the oops-log into stdin.  Out
  24 // will come the EIP and call-trace in symbolic form.
  25 
  26 //////////////////////////////////////////////////////////////////////////////
  27 
  28 // BUGS:
  29 // * Doesn't deal with line-prefixes prepended by syslog--strip
  30 //   these off first, before submitting to ksymoops.
  31 // * Only resolves operands of jump and call instructions.
  32 
  33 #include <fstream.h>
  34 #include <iomanip.h>
  35 #include <stdio.h>
  36 #include <string.h>
  37 #include <stdlib.h>
  38 #include <unistd.h>
  39 #include <ctype.h>
  40 
  41 inline int strequ(char const* x, char const* y) { return (::strcmp(x, y) == 0); }
     /* [<][>][^][v][top][bottom][index][help] */
  42 inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); }
     /* [<][>][^][v][top][bottom][index][help] */
  43 
  44 const int code_size = 20;
  45 
  46 //////////////////////////////////////////////////////////////////////////////
  47 
  48 class KSym
  49 {
  50     friend class NameList;
  51 
  52   private:
  53     long address_;
  54     char* name_;
  55     long offset_;
  56     long extent_;
  57 
  58   private:
  59     istream& scan(istream&);
  60     ostream& print(ostream&) const;
  61     void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; }
     /* [<][>][^][v][top][bottom][index][help] */
  62 
  63   public:
  64     friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); }
  65     friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); }
  66 };
  67 
  68 istream&
  69 KSym::scan(istream& is)
     /* [<][>][^][v][top][bottom][index][help] */
  70 {
  71     is >> ::hex >> address_;
  72     char type;
  73     is >> type;
  74     char name[128];
  75     is >> name;
  76     name_ = new char [strlen(name)+1];
  77     strcpy(name_, name);
  78     offset_ = 0;
  79     return is;
  80 }
  81 
  82 ostream&
  83 KSym::print(ostream& os) const
     /* [<][>][^][v][top][bottom][index][help] */
  84 {
  85     os << ::hex << address_ + offset_ << ' ' << '<' << name_;
  86     if (offset_)
  87         os << '+' << ::hex << offset_ << '/' << ::hex << extent_;
  88     return os << '>';
  89 }
  90 
  91 //////////////////////////////////////////////////////////////////////////////
  92 
  93 class NameList
  94 {
  95   private:
  96     // Caution: Fixed Allocation!
  97     // This should suffice for awhile since 1.1.86 has only 2482 symbols.
  98     KSym ksyms_0_[4096];
  99     int cardinality_;
 100 
 101   public:
 102     NameList() : cardinality_(0) { }
 103     
 104   private:
 105     istream& scan(istream&);
 106 
 107   public:
 108     int valid() { return (cardinality_ > 0); }
     /* [<][>][^][v][top][bottom][index][help] */
 109         
 110     KSym* find(long address);
 111     void decode(unsigned char* code, long eip_addr);
 112     
 113   public:
 114     friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); }
 115 };
 116 
 117 KSym*
 118 NameList::find(long address)
     /* [<][>][^][v][top][bottom][index][help] */
 119 {
 120     if (!valid())
 121         return 0;
 122     KSym* start = ksyms_0_;
 123     KSym* end = &ksyms_0_[cardinality_ - 1];
 124     if (address < start->address_ || address >= end->address_)
 125         return 0;
 126 
 127     KSym* mid;
 128     while (start <= end) {
 129         mid = &start[(end - start) / 2];
 130         if (mid->address_ < address)
 131             start = mid + 1;
 132         else if (mid->address_ > address)
 133             end = mid - 1;
 134         else
 135             return mid;
 136     }
 137     while (mid->address_ > address)
 138         --mid;
 139     mid->offset_ = address - mid->address_;
 140     if (mid->offset_ > mid->extent_)
 141         clog << "Oops! " << *mid << endl;
 142     return mid;
 143 }
 144 
 145 void
 146 NameList::decode(unsigned char* code, long eip_addr)
     /* [<][>][^][v][top][bottom][index][help] */
 147 {
 148     /* This is a hack to avoid using gcc.  We create an object file by
 149        concatenating objfile_head, the twenty bytes of code, and
 150        objfile_tail.  */
 151     unsigned char objfile_head[] = {
 152         0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
 153         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 154         0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 155         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 156     };
 157     unsigned char objfile_tail[] = {
 158         0x00, 0x90, 0x90, 0x90,
 159         0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
 160         0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
 161         0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 162         0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
 163         0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
 164         'g',  'c',  'c',  '2',  '_',  'c',  'o',  'm',  
 165         'p',  'i',  'l',  'e',  'd',  '.',  '\0', '_',  
 166         'E',  'I',  'P',  '\0', '\0', '\0', '\0', '\0',
 167         '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
 168         '\0', '\0', '\0', '\0', '\0', '\0'
 169     };
 170     char const* objdump_command = "objdump -d oops_decode.o";
 171     char const* objfile_name = &objdump_command[11];
 172     ofstream objfile_stream(objfile_name);
 173 
 174     objfile_stream.write(objfile_head, sizeof(objfile_head));
 175     objfile_stream.write(code, code_size);
 176     objfile_stream.write(objfile_tail, sizeof(objfile_tail));
 177     objfile_stream.close();
 178     
 179     FILE* objdump_FILE = popen(objdump_command, "r");
 180     if (objdump_FILE == 0) {
 181         clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl;
 182         return;
 183     }
 184     
 185     char buf[1024];
 186     int lines = 0;
 187     while (fgets(buf, sizeof(buf), objdump_FILE)) {
 188         if (!strnequ(&buf[9], "<_EIP", 5))
 189             continue;
 190         if (strstr(buf, " is out of bounds"))
 191             break;
 192         lines++;
 193         cout << "Code: ";
 194         if (!valid()) {
 195             cout << buf;
 196             continue;
 197         }
 198         long offset = strtol(buf, 0, 16);
 199         char* bp_0 = strchr(buf, '>') + 2;
 200         KSym* ksym = find(eip_addr + offset);
 201         if (ksym)
 202             cout << *ksym << ' ';
 203         char* bp = bp_0;
 204         while (!isspace(*bp))
 205             bp++;
 206         while (isspace(*bp))
 207             bp++;
 208         if (*bp != '0') {
 209             cout << bp_0;
 210         } else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
 211             long rel_addr = strtol(bp, 0, 16);
 212             ksym = find(eip_addr + rel_addr);
 213             if (ksym) {
 214                 *bp++ = '\0';
 215                 cout << bp_0 << *ksym << endl;
 216             } else
 217                 cout << bp_0;
 218         } else {
 219             cout << bp_0;
 220         }
 221     }
 222     if (!lines)
 223         clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl;
 224     pclose(objdump_FILE);
 225     unlink(objfile_name);
 226 }
 227 
 228 istream&
 229 NameList::scan(istream& is)
     /* [<][>][^][v][top][bottom][index][help] */
 230 {
 231     KSym* ksyms = ksyms_0_;
 232     int cardinality = 0;
 233     while (!is.eof()) {
 234         is >> *ksyms;
 235         if (cardinality++ > 0)
 236             ksyms[-1].set_extent(*ksyms);
 237         ksyms++;
 238     }
 239     cardinality_ = --cardinality;
 240     return is;
 241 }
 242 
 243 //////////////////////////////////////////////////////////////////////////////
 244 
 245 char const* program_name;
 246 
 247 void
 248 usage()
     /* [<][>][^][v][top][bottom][index][help] */
 249 {
 250     clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl;
 251     exit(1);
 252 }
 253 
 254 int
 255 main(int argc, char** argv)
     /* [<][>][^][v][top][bottom][index][help] */
 256 {
 257     program_name = (argc--, *argv++);
 258 
 259     NameList names;
 260     if (argc > 1)
 261         usage();
 262     else if (argc == 1) {
 263         char const* map_file_name = (argc--, *argv++);
 264         ifstream map(map_file_name);
 265         if (map.bad())
 266             clog << program_name << ": Can't open `" << map_file_name << "'" << endl;
 267         else {
 268             map >> names;
 269             cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl;
 270         }
 271     }
 272     if (!names.valid())
 273         cout << "No symbol map.  I'll only show you disassembled code." << endl;
 274     cout << endl;
 275 
 276     char buffer[1024];
 277     while (!cin.eof())
 278     {
 279         long eip_addr;
 280         cin >> buffer;
 281         if (strequ(buffer, "EIP:") && names.valid()) {
 282             cin >> ::hex >> eip_addr;
 283             cin >> buffer[0];
 284             cin >> ::hex >> eip_addr;
 285             cin >> buffer;
 286             if (!strequ(buffer, "EFLAGS:")) {
 287                 clog << "Please strip the line-prefixes and rerun " << program_name << endl;
 288                 exit(1);
 289             }
 290             KSym* ksym = names.find(eip_addr);
 291             if (ksym)
 292                 cout << ">>EIP: " << *ksym << endl;
 293         } else if (strequ(buffer, "Trace:") && names.valid()) {
 294             long address;
 295             while ((cin >> ::hex >> address) && address > 0xc) {
 296                 cout << "Trace: ";
 297                 KSym* ksym = names.find(address);
 298                 if (ksym)
 299                     cout << *ksym;
 300                 else
 301                     cout << ::hex << address;
 302                 cout << endl;
 303             }
 304             cout << endl;
 305         } else if (strequ(buffer, "ode:") || strequ(buffer, "Code:")) {
 306             // The 'C' might have been consumed as a hex number
 307             unsigned char code[code_size];
 308             unsigned char* cp = code;
 309             unsigned char* end = &code[code_size];
 310             while (cp < end) {
 311                 int c;
 312                 cin >> ::hex >> c;
 313                 *cp++ = c;
 314             }
 315             names.decode(code, eip_addr);
 316         }
 317     }
 318     cout << flush;
 319 
 320     return 0;
 321 }

/* [<][>][^][v][top][bottom][index][help] */