patch-1.3.32 linux/scripts/ksymoops.cc

Next file: linux/CREDITS
Previous file: linux/net/ipv4/ip.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.31/linux/scripts/ksymoops.cc linux/scripts/ksymoops.cc
@@ -1,31 +1,47 @@
-/* ksymoops.c -- simple Linux Oops-log symbol resolver
-   Copyright (C) 1995 Greg McGary
-  
-   This program 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; either version 2, or (at your option)
-   any later version.
-  
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-  
-   You should have received a copy of the GNU General Public License
-   along with this program; see the file COPYING.  If not, write to the
-   Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-/* This is a simple filter to resolve EIP and call-trace symbols from
-   a Linux kernel "Oops" log.  Supply the symbol-map file name as a
-   command-line argument, and redirect the oops-log into stdin.
-   Out will come the EIP and call-trace in symbolic form.  */
+// ksymoops.cc v1.7 -- A simple filter to resolve symbols in Linux Oops-logs
+// Copyright (C) 1995 Greg McGary <gkm@magilla.cichlid.com>
+// compile like so: g++ -o ksymoops ksymoops.cc -liostream
+
+//////////////////////////////////////////////////////////////////////////////
+
+// This program 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; either version 2, or (at your option)
+// any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; see the file COPYING.  If not, write to the
+// Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+// This is a simple filter to resolve EIP and call-trace symbols from
+// a Linux kernel "Oops" log.  Supply the symbol-map file name as a
+// command-line argument, and redirect the oops-log into stdin.  Out
+// will come the EIP and call-trace in symbolic form.
+
+//////////////////////////////////////////////////////////////////////////////
+
+// BUGS:
+// * Doesn't deal with line-prefixes prepended by syslog--strip
+//   these off first, before submitting to ksymoops.
+// * Only resolves operands of jump and call instructions.
 
 #include <fstream.h>
+#include <iomanip.h>
+#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
 
 inline int strequ(char const* x, char const* y) { return (::strcmp(x, y) == 0); }
+inline int strnequ(char const* x, char const* y, size_t n) { return (::strncmp(x, y, n) == 0); }
+
+const int code_size = 20;
 
 //////////////////////////////////////////////////////////////////////////////
 
@@ -35,37 +51,41 @@
 
   private:
     long address_;
-    char type_;
     char* name_;
     long offset_;
     long extent_;
 
-  public:
+  private:
+    istream& scan(istream&);
+    ostream& print(ostream&) const;
     void set_extent(KSym const& next_ksym) { extent_ = next_ksym.address_ - address_; }
-    friend istream& operator >> (istream&, KSym&);
-    friend ostream& operator << (ostream&, const KSym&);
+
+  public:
+    friend istream& operator >> (istream& is, KSym& k) { return k.scan(is); }
+    friend ostream& operator << (ostream& os, const KSym& k) { return k.print(os); }
 };
 
 istream&
-operator >> (istream& is, KSym& n)
+KSym::scan(istream& is)
 {
-    is >> hex >> n.address_;
-    is >> n.type_;
+    is >> ::hex >> address_;
+    char type;
+    is >> type;
     char name[128];
     is >> name;
-    n.name_ = new char [strlen(name)+1];
-    strcpy(n.name_, name);
-    n.offset_ = 0;
+    name_ = new char [strlen(name)+1];
+    strcpy(name_, name);
+    offset_ = 0;
     return is;
 }
 
 ostream&
-operator << (ostream& os, const KSym& n)
+KSym::print(ostream& os) const
 {
-    os << hex << n.address_ + n.offset_ << ' ' << n.type_ << ' ' << n.name_;
-    if (n.offset_)
-	os << '+' << hex << n.offset_ << '/' << hex << n.extent_;
-    return os;
+    os << ::hex << address_ + offset_ << ' ' << '<' << name_;
+    if (offset_)
+	os << '+' << ::hex << offset_ << '/' << ::hex << extent_;
+    return os << '>';
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -81,20 +101,30 @@
   public:
     NameList() : cardinality_(0) { }
     
+  private:
+    istream& scan(istream&);
+
   public:
+    int valid() { return (cardinality_ > 0); }
+	
     KSym* find(long address);
+    void decode(unsigned char* code, long eip_addr);
     
   public:
-    friend istream& operator >> (istream&, NameList&);
+    friend istream& operator >> (istream& is, NameList& n) { return n.scan(is); }
 };
 
 KSym*
 NameList::find(long address)
 {
+    if (!valid())
+	return 0;
     KSym* start = ksyms_0_;
-    KSym* end = &ksyms_0_[cardinality_];
-    KSym* mid;
+    KSym* end = &ksyms_0_[cardinality_ - 1];
+    if (address < start->address_ || address >= end->address_)
+	return 0;
 
+    KSym* mid;
     while (start <= end) {
 	mid = &start[(end - start) / 2];
 	if (mid->address_ < address)
@@ -112,18 +142,101 @@
     return mid;
 }
 
+void
+NameList::decode(unsigned char* code, long eip_addr)
+{
+    /* This is a hack to avoid using gcc.  We create an object file by
+       concatenating objfile_head, the twenty bytes of code, and
+       objfile_tail.  */
+    unsigned char objfile_head[] = {
+	0x07, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    };
+    unsigned char objfile_tail[] = {
+	0x00, 0x90, 0x90, 0x90,
+	0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x25, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+	'g',  'c',  'c',  '2',  '_',  'c',  'o',  'm',  
+	'p',  'i',  'l',  'e',  'd',  '.',  '\0', '_',  
+	'E',  'I',  'P',  '\0', '\0', '\0', '\0', '\0',
+	'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+	'\0', '\0', '\0', '\0', '\0', '\0'
+    };
+    char const* objdump_command = "objdump -d oops_decode.o";
+    char const* objfile_name = &objdump_command[11];
+    ofstream objfile_stream(objfile_name);
+
+    objfile_stream.write(objfile_head, sizeof(objfile_head));
+    objfile_stream.write(code, code_size);
+    objfile_stream.write(objfile_tail, sizeof(objfile_tail));
+    objfile_stream.close();
+    
+    FILE* objdump_FILE = popen(objdump_command, "r");
+    if (objdump_FILE == 0) {
+	clog << "Sorry, without " << objdump_command << ", I can't disassemble the `Code' section." << endl;
+	return;
+    }
+    
+    char buf[1024];
+    int lines = 0;
+    while (fgets(buf, sizeof(buf), objdump_FILE)) {
+	if (!strnequ(&buf[9], "<_EIP", 5))
+	    continue;
+	if (strstr(buf, " is out of bounds"))
+	    break;
+	lines++;
+	cout << "Code: ";
+	if (!valid()) {
+	    cout << buf;
+	    continue;
+	}
+	long offset = strtol(buf, 0, 16);
+	char* bp_0 = strchr(buf, '>') + 2;
+	KSym* ksym = find(eip_addr + offset);
+	if (ksym)
+	    cout << *ksym << ' ';
+	char* bp = bp_0;
+	while (!isspace(*bp))
+	    bp++;
+	while (isspace(*bp))
+	    bp++;
+	if (*bp != '0') {
+	    cout << bp_0;
+	} else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn
+	    long rel_addr = strtol(bp, 0, 16);
+	    ksym = find(eip_addr + rel_addr);
+	    if (ksym) {
+		*bp++ = '\0';
+		cout << bp_0 << *ksym << endl;
+	    } else
+		cout << bp_0;
+	} else {
+	    cout << bp_0;
+	}
+    }
+    if (!lines)
+	clog << "Sorry, your " << objdump_command << " can't disassemble--you must upgrade your binutils." << endl;
+    pclose(objdump_FILE);
+    unlink(objfile_name);
+}
+
 istream&
-operator >> (istream& is, NameList& n)
+NameList::scan(istream& is)
 {
-    KSym* ksyms = n.ksyms_0_;
+    KSym* ksyms = ksyms_0_;
     int cardinality = 0;
     while (!is.eof()) {
 	is >> *ksyms;
-	ksyms[-1].set_extent(*ksyms);
+	if (cardinality++ > 0)
+	    ksyms[-1].set_extent(*ksyms);
 	ksyms++;
-	cardinality++;
     }
-    n.cardinality_ = --cardinality;
+    cardinality_ = --cardinality;
     return is;
 }
 
@@ -134,7 +247,7 @@
 void
 usage()
 {
-    clog << "Usage: " << program_name << " system-map-file < oops-log" << endl;
+    clog << "Usage: " << program_name << " [ System.map ] < oops-log" << endl;
     exit(1);
 }
 
@@ -142,38 +255,64 @@
 main(int argc, char** argv)
 {
     program_name = (argc--, *argv++);
-    if (argc != 1)
-	usage();
 
-    char const* map_file_name = (argc--, *argv++);
-    ifstream map(map_file_name);
-    if (map.bad()) {
-	clog << program_name << ": Can't open `" << map_file_name << "'" << endl;
-	return 1;
-    }
-    
     NameList names;
-    map >> names;
+    if (argc > 1)
+	usage();
+    else if (argc == 1) {
+	char const* map_file_name = (argc--, *argv++);
+	ifstream map(map_file_name);
+	if (map.bad())
+	    clog << program_name << ": Can't open `" << map_file_name << "'" << endl;
+	else {
+	    map >> names;
+	    cout << "Using `" << map_file_name << "' to map addresses to symbols." << endl;
+	}
+    }
+    if (!names.valid())
+	cout << "No symbol map.  I'll only show you disassembled code." << endl;
+    cout << endl;
 
     char buffer[1024];
     while (!cin.eof())
     {
-	long address;
+	long eip_addr;
 	cin >> buffer;
-	if (strequ(buffer, "EIP:")) {
-	    cin >> hex >> address;
+	if (strequ(buffer, "EIP:") && names.valid()) {
+	    cin >> ::hex >> eip_addr;
 	    cin >> buffer[0];
-	    cin >> hex >> address;
-	    KSym* ksym = names.find(address);
+	    cin >> ::hex >> eip_addr;
+	    cin >> buffer;
+	    if (!strequ(buffer, "EFLAGS:")) {
+		clog << "Please strip the line-prefixes and rerun " << program_name << endl;
+		exit(1);
+	    }
+	    KSym* ksym = names.find(eip_addr);
 	    if (ksym)
-		cout << "EIP: " << *ksym << endl;
-	} else if (strequ(buffer, "Trace:")) {
-	    while ((cin >> address) && address > 0xc) {
+		cout << ">>EIP: " << *ksym << endl;
+	} else if (strequ(buffer, "Trace:") && names.valid()) {
+	    long address;
+	    while ((cin >> ::hex >> address) && address > 0xc) {
+		cout << "Trace: ";
 		KSym* ksym = names.find(address);
 		if (ksym)
-		    cout << "Trace: " << *ksym << endl;
+		    cout << *ksym;
+		else
+		    cout << ::hex << address;
+		cout << endl;
 	    }
 	    cout << endl;
+	} else if (strequ(buffer, "ode:") || strequ(buffer, "Code:")) {
+	    // The 'C' might have been consumed as a hex number
+	    unsigned char code[code_size];
+	    unsigned char* cp = code;
+	    unsigned char* end = &code[code_size];
+	    while (cp < end) {
+		int c;
+		cin >> ::hex >> c;
+		*cp++ = c;
+	    }
+	    names.decode(code, eip_addr);
 	}
     }
     cout << flush;


FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this