linux-old/scripts/ksymoops.cc
<<
>>
Prefs
   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 <strstream.h>
  35#include <iomanip.h>
  36#include <stdio.h>
  37#include <string.h>
  38#include <stdlib.h>
  39#include <unistd.h>
  40#include <ctype.h>
  41
  42inline int strequ(char const* x, char const* y) { return (::strcmp(x, y) == 0); }
  43inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); }
  44
  45const int code_size = 20;
  46
  47//////////////////////////////////////////////////////////////////////////////
  48
  49class KSym
  50{
  51    friend class NameList;
  52
  53  private:
  54    unsigned long address_;
  55    char* name_;
  56    long offset_;
  57    long extent_;
  58
  59  private:
  60    istream& scan(istream&);
  61    ostream& print(ostream&) const;
  62    void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; }
  63
  64  public:
  65    friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); }
  66    friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); }
  67};
  68
  69istream&
  70KSym::scan(istream& is)
  71{
  72    is >> ::hex >> address_;
  73    char type;
  74    is >> type;
  75    char name[128];
  76    is >> name;
  77    name_ = new char [strlen(name)+1];
  78    strcpy(name_, name);
  79    offset_ = 0;
  80    return is;
  81}
  82
  83ostream&
  84KSym::print(ostream& os) const
  85{
  86    os << ::hex << address_ + offset_ << ' ' << '<' << name_;
  87    if (offset_)
  88        os << '+' << ::hex << offset_ << '/' << ::hex << extent_;
  89    return os << '>';
  90}
  91
  92//////////////////////////////////////////////////////////////////////////////
  93
  94class NameList
  95{
  96  private:
  97    // Caution: Fixed Allocation!
  98    // This should suffice for awhile since 1.1.86 has only 2482 symbols.
  99    KSym ksyms_0_[8000];
 100    int cardinality_;
 101
 102  public:
 103    NameList() : cardinality_(0) { }
 104    
 105  private:
 106    istream& scan(istream&);
 107
 108  public:
 109    int valid() { return (cardinality_ > 0); }
 110        
 111    KSym* find(unsigned long address);
 112    void decode(unsigned char* code, long eip_addr);
 113    
 114  public:
 115    friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); }
 116};
 117
 118KSym*
 119NameList::find(unsigned long address)
 120{
 121    if (!valid())
 122        return 0;
 123    KSym* start = ksyms_0_;
 124    KSym* end = &ksyms_0_[cardinality_ - 1];
 125    if (address < start->address_ || address >= end->address_)
 126        return 0;
 127
 128    KSym* mid;
 129    while (start <= end) {
 130        mid = &start[(end - start) / 2];
 131        if (mid->address_ < address)
 132            start = mid + 1;
 133        else if (mid->address_ > address)
 134            end = mid - 1;
 135        else
 136            return mid;
 137    }
 138    while (mid->address_ > address)
 139        --mid;
 140    mid->offset_ = address - mid->address_;
 141    if (mid->offset_ > mid->extent_)
 142        clog << "Oops! " << *mid << endl;
 143    return mid;
 144}
 145
 146void
 147NameList::decode(unsigned char* code, long eip_addr)
 148{
 149    /* This is a hack to avoid using gcc.  We create an object file by
 150       concatenating objfile_head, the twenty bytes of code, and
 151       objfile_tail.  */
 152    unsigned char objfile_head[] = {
 153        0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
 154        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 155        0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 156        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 157    };
 158    unsigned char objfile_tail[] = {
 159        0x00, 0x90, 0x90, 0x90,
 160        0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
 161        0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
 162        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 163        0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
 164        0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
 165        'g',  'c',  'c',  '2',  '_',  'c',  'o',  'm',  
 166        'p',  'i',  'l',  'e',  'd',  '.',  '\0', '_',  
 167        'E',  'I',  'P',  '\0', '\0', '\0', '\0', '\0',
 168        '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
 169        '\0', '\0', '\0', '\0', '\0', '\0'
 170    };
 171    char const* objdump_command = "objdump -d oops_decode.o";
 172    char const* objfile_name = &objdump_command[11];
 173    ofstream objfile_stream(objfile_name);
 174
 175    objfile_stream.write(objfile_head, sizeof(objfile_head));
 176    objfile_stream.write(code, code_size);
 177    objfile_stream.write(objfile_tail, sizeof(objfile_tail));
 178    objfile_stream.close();
 179    
 180    FILE* objdump_FILE = popen(objdump_command, "r");
 181    if (objdump_FILE == 0) {
 182        clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl;
 183        return;
 184    }
 185    
 186    char buf[1024];
 187    int lines = 0;
 188    int eip_seen = 0;
 189    long offset;
 190    while (fgets(buf, sizeof(buf), objdump_FILE)) {
 191        if (eip_seen && buf[4] == ':') {
 192            // assume objdump from binutils 2.8..., reformat to old style
 193            offset = strtol(buf, 0, 16);
 194            char newbuf[sizeof(buf)];
 195            memset(newbuf, '\0', sizeof(newbuf));
 196            ostrstream ost(newbuf, sizeof(newbuf));
 197            ost.width(8);
 198            ost << offset;
 199            ost << " <_EIP+" << offset << ">: " << &buf[6] << ends;
 200            strcpy(buf, newbuf);
 201        }
 202        if (!strnequ(&buf[9], "<_EIP", 5))
 203            continue;
 204        eip_seen = 1;
 205        if (strstr(buf, " is out of bounds"))
 206            break;
 207        lines++;
 208        cout << "Code: ";
 209        if (!valid()) {
 210            cout << buf;
 211            continue;
 212        }
 213        offset = strtol(buf, 0, 16);
 214        char* bp_0 = strchr(buf, '>');
 215        KSym* ksym = find(eip_addr + offset);
 216        if (bp_0)
 217            bp_0 += 2;
 218        else
 219            bp_0 = strchr(buf, ':');
 220        if (ksym)
 221            cout << *ksym << ' ';
 222        char *bp_1 = strstr(bp_0, "\t");        // objdump from binutils 2.8...
 223        if (bp_1)
 224            ++bp_1;
 225        else
 226            bp_1 = bp_0;
 227        char *bp = bp_1;
 228        while (!isspace(*bp))
 229            bp++;
 230        while (isspace(*bp))
 231            bp++;
 232        if (!isxdigit(*bp)) {
 233            cout << bp_0;
 234        } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn
 235            long rel_addr = strtol(bp, 0, 16);
 236            ksym = find(eip_addr + rel_addr);
 237            if (ksym) {
 238                *bp++ = '\0';
 239                cout << bp_0 << *ksym << endl;
 240            } else
 241                cout << bp_0;
 242        } else {
 243            cout << bp_0;
 244        }
 245    }
 246    if (!lines)
 247        clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl;
 248    pclose(objdump_FILE);
 249    unlink(objfile_name);
 250}
 251
 252istream&
 253NameList::scan(istream& is)
 254{
 255    KSym* ksyms = ksyms_0_;
 256    int cardinality = 0;
 257    while (!is.eof()) {
 258        is >> *ksyms;
 259        if (cardinality++ > 0)
 260            ksyms[-1].set_extent(*ksyms);
 261        ksyms++;
 262    }
 263    cardinality_ = --cardinality;
 264    return is;
 265}
 266
 267//////////////////////////////////////////////////////////////////////////////
 268
 269char const* program_name;
 270
 271void
 272usage()
 273{
 274    clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl;
 275    exit(1);
 276}
 277
 278int
 279main(int argc, char** argv)
 280{
 281    char c;
 282    program_name = (argc--, *argv++);
 283
 284    NameList names;
 285    if (argc > 1)
 286        usage();
 287    else if (argc == 1) {
 288        char const* map_file_name = (argc--, *argv++);
 289        ifstream map(map_file_name);
 290        if (map.bad())
 291            clog << program_name << ": Can't open `" << map_file_name << "'" << endl;
 292        else {
 293            map >> names;
 294            cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl;
 295        }
 296    }
 297    if (!names.valid())
 298        cout << "No symbol map.  I'll only show you disassembled code." << endl;
 299    cout << endl;
 300
 301    char buffer[1024];
 302    while (!cin.eof())
 303    {
 304        long eip_addr;
 305        cin >> buffer;
 306        if (strequ(buffer, "EIP:") && names.valid()) {
 307            cin >> ::hex >> eip_addr;
 308            cin >> c >> c >> c;
 309            cin >> ::hex >> eip_addr;
 310            cin >> c >> c >> buffer;
 311            if (!strequ(buffer, "EFLAGS:")) {
 312                clog << "Please strip the line-prefixes and rerun " << program_name << endl;
 313                exit(1);
 314            }
 315            KSym* ksym = names.find(eip_addr);
 316            if (ksym)
 317                cout << ">>EIP: " << *ksym << endl;
 318        } else if (strequ(buffer, "Trace:") && names.valid()) {
 319            unsigned long address;
 320            while ((cin >> buffer) && 
 321                   (sscanf(buffer, " [<%x>]", &address) == 1) &&
 322                   address > 0xc) {
 323                cout << "Trace: ";
 324                KSym* ksym = names.find(address);
 325                if (ksym)
 326                    cout << *ksym;
 327                else
 328                    cout << ::hex << address;
 329                cout << endl;
 330            }
 331            cout << endl;
 332        }
 333        if (strequ(buffer, "ode:") || strequ(buffer, "Code:")) {
 334            // The 'C' might have been consumed as a hex number
 335            unsigned char code[code_size];
 336            unsigned char* cp = code;
 337            unsigned char* end = &code[code_size];
 338            while (cp < end) {
 339                int c;
 340                cin >> ::hex >> c;
 341                *cp++ = c;
 342            }
 343            names.decode(code, eip_addr);
 344        }
 345    }
 346    cout << flush;
 347
 348    return 0;
 349}
 350
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.