lkml.org 
[lkml]   [1997]   [Oct]   [30]   [last100]   RSS Feed
Views: [more markup]  [less markup]  [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
DateThu, 30 Oct 1997 23:59:30 +0100 (MET)
FromGerd Knorr <>
Subjectnew oops decoder
  Hi !

I've written a new oops decoder. Features:

  * It is written in C, not C++. You can wipe g++ now it you used it for
    ksymoops only :-)
  * It comes with a help text (-h switch)
  * it can handle more than one oops without segfaults.
  * it can handle modules.

The module thing works with a wrapper script for insmod. It uses insmod's
-m switch to dump the module loadmaps to a directory. It is a good idea to
add a few lines to the rc scripts to hold a small history of the loadmaps.
That way you can decode the oops even after rebooting.

It works much like ksymoops.  It expects dmesg output or /var/log/messages
on stdin.  Everything (except switches) on the command line is assumed to
be a symbol file (either System.map or some module loadmap).  If the
system is still running after the oops, you can use the '-m' switch to
load the module symbols (uses /proc/modules to check which modules are
loaded).

  Gerd

----------------------------------------------------------------------
diff -urN kernel/2.1.60/scripts/insmod-wrapper linux/scripts/insmod-wrapper
--- kernel/2.1.60/scripts/insmod-wrapper	Thu Jan  1 01:00:00 1970
+++ linux/scripts/insmod-wrapper	Thu Oct 30 23:39:07 1997
@@ -0,0 +1,67 @@
+#!/bin/sh
+# insmod wrapper for loadmap trickery
+# usage:
+#   1) rename insmod to insmod-real
+#   2) copy or symlink this script to insmod
+#
+# then the script will dump all module loadmaps to $MAPDIR
+#
+#######################################################################
+
+# destination directory
+MAPDIR=/lib/modules/loadmaps
+
+# the real thing
+INSMOD=/sbin/insmod-real
+
+#######################################################################
+OPTS=""
+ARGS=""
+MAP=""
+DEST=""
+
+CALLED="$0 $*"
+
+set -- `getopt fkmpsxXvo: $*` || exit 1
+while [ "$1" != "" ]; do
+	case $1 in
+		-f | -k | -p | -x | -X | -v)
+			# pass throuth
+			OPTS="$OPTS $1"; shift ;;
+		-o)
+			# pass throuth with arg, note name
+			OPTS="$OPTS $1 $2"; DEST="$2"; shift; shift;;
+		-m)
+			# note this, no file but stdout for loadmap
+			OPTS="$OPTS $1"; MAP="nofile"; shift ;;
+		-s)
+			# skip - no syslog becauce we want dump to a file
+			shift;;
+		--)
+			# grab module name for loadmap filename
+			MOD=$2; shift; shift;;
+		*=*)
+			# some module arg
+			ARGS="$ARGS $1"; shift;;
+		*)
+			# Huh?
+			echo "insmod-wrapper: what the heck is '$1' ???"
+			echo "command line: $CALLED"
+			exit 1;;
+	esac
+done
+
+if [ "$MAP" = "nofile" ]; then
+	# -m on command line -- dump loadmap to stdout (to make it
+	# work like people expect from docs)
+	$INSMOD $OPTS $MOD $ARGS
+else
+	# dump it to our directory
+	[ "$DEST" = "" ] && DEST="`basename $MOD .o`"
+	DESTFILE="$MAPDIR/$DEST"
+	echo "orig     : $CALLED"			>  $DESTFILE
+	echo "calling  : $INSMOD $OPTS -m $MOD $ARGS"	>> $DESTFILE
+	echo "timestamp: `date`"			>> $DESTFILE
+	echo "--"					>> $DESTFILE
+	$INSMOD $OPTS -m $MOD $ARGS			>> $DESTFILE
+fi
diff -urN kernel/2.1.60/scripts/module-deoops.c linux/scripts/module-deoops.c
--- kernel/2.1.60/scripts/module-deoops.c	Thu Jan  1 01:00:00 1970
+++ linux/scripts/module-deoops.c	Thu Oct 30 23:39:24 1997
@@ -0,0 +1,385 @@
+/*
+ * linux kernel+modules oops decoder, i386 only
+ *
+ * requires:
+ *   - gcc + gdb for decoding the Code: line
+ *   - insmod-wrapper installed for module symbols
+ *
+ * description:
+ *   run it with -h
+ *
+ * (c) 1997 Gerd Knorr <kraxel@cs.tu-berlin.de>
+ *
+ * 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 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* --------------------------------------------------------------------- */
+/* structs                                                               */
+
+typedef unsigned long addr_type;
+
+struct SYMFILE {
+    char fullname[256];
+    char *basename;
+    unsigned long start,end;
+};
+
+struct SYMBOL {
+    addr_type         addr;
+    char              name[40];
+    char              type;
+    struct SYMFILE   *file;
+};
+
+struct SYMFILE  **symfiles;
+struct SYMBOL   **symbols;
+int filecount, symbolcount;
+
+/* --------------------------------------------------------------------- */
+/* file and symbol handling                                              */
+
+struct SYMBOL*
+add_symbol(unsigned long addr, char type, char *name, struct SYMFILE *file)
+{
+    struct SYMBOL *sym;
+    
+    if (0 == (symbolcount % 1024))
+	symbols = realloc(symbols,(symbolcount+1024)*sizeof(struct SYMBOL*));
+    sym = symbols[symbolcount++] = malloc(sizeof(struct SYMBOL));
+
+    strcpy(sym->name,name);
+    sym->addr = addr;
+    sym->type = type;
+    sym->file = file;
+    return sym;
+}
+
+int
+lookup_symbol(unsigned long addr)
+{
+    int i;
+
+    for (i = 0; i < symbolcount; i++) {
+	if (symbols[i]->addr > addr) {
+	    i--;
+	    break;
+	}
+    }
+    if (i == symbolcount)
+	return -1;
+    return i;
+}
+
+void
+print_symbol(addr_type addr)
+{
+    int nr;
+
+    nr = lookup_symbol(addr);
+    printf("0x%08lx ",addr);
+    if (-1 == nr) {
+	printf("???");
+    } else {
+	printf("%c ",symbols[nr]->type);
+	if (symbols[nr]->file->start)
+	    printf("(%s+)",symbols[nr]->file->basename);
+	printf("%s+0x%lx/0x%lx",
+	       symbols[nr]->name,
+	       addr-(symbols[nr]->addr),
+	       (symbols[nr+1]->addr)-(symbols[nr]->addr));
+    }
+}
+
+int
+compare_symbols(const void *a, const void *b)
+{
+    addr_type aa = (*((struct SYMBOL**)a))->addr;
+    addr_type bb = (*((struct SYMBOL**)b))->addr;
+    if (aa == bb)
+	return 0;
+    return (aa < bb) ? -1 : 1;
+}
+
+struct SYMFILE*
+add_symfile(char *name)
+{
+    struct SYMFILE *file;
+    
+    if (0 == (filecount % 16))
+	symfiles = realloc(symfiles,(filecount+16)*sizeof(struct SYMFILE*));
+    file = symfiles[filecount] = malloc(sizeof(struct SYMFILE));
+
+    strcpy(file->fullname, name);
+    if (NULL == (file->basename = strrchr(file->fullname,'/')))
+	file->basename = file->fullname;
+    else
+	file->basename++;
+    file->start = 0;
+    file->end = 0;
+    return file;
+}
+
+void
+load_file(char *filename)
+{
+    FILE            *fp;
+    struct SYMFILE  *file;
+    char            line[128];
+    char            type[2], name[40], section[20];
+    int             count = 0;
+    addr_type       size,base,addr;
+
+    if (NULL == (fp = fopen(filename,"r"))) {
+	printf("can't open %s: %s\n",filename,strerror(errno));
+	return;
+    }
+    file = add_symfile(filename);
+    
+    while (NULL != fgets(line,127,fp)) {
+	/* symbols */
+	if (3 == sscanf(line,"%lx %1[BDTbdt] %39s",
+			&addr,type,name)) {
+	    add_symbol(addr,type[0],name,file);
+	    count++;
+	}
+	/* module: sections */
+	if (3 == sscanf(line,".%19[a-z] %lx %lx",section,&size,&base)) {
+	    if (!file->start)
+		file->start = base;
+	    file->end = base+size;
+	}
+    }
+    
+    if (file->start) {
+	printf("%4d symbols [%lx-%lx] from %s\n",
+	       count,file->start,file->end,filename);
+    } else {
+	printf("%4d symbols from %s\n",
+	       count,filename);
+    }
+    fclose(fp);
+}
+
+void
+load_proc_modules(char *filename, char *path)
+{
+    FILE *fp;
+    char module[256];
+    char line[128],*h;
+
+    if (NULL == (fp = fopen(filename,"r"))) {
+	printf("can't open %s: %s\n",filename,strerror(errno));
+	return;
+    }
+    
+    while (NULL != fgets(line,127,fp)) {
+	h = strchr(line,' ');
+	if (h) {
+	    *h = '\0';
+	    strcpy(module,path);
+	    strcat(module,"/");
+	    strcat(module,line);
+	    load_file(module);
+	}
+    }
+    fclose(fp);
+}
+
+/* --------------------------------------------------------------------- */
+/* decoder itself                                                        */
+
+#define DISFILE "/tmp/decode-oops"
+
+void
+disasm(int *code, int len)
+{
+    FILE *fp;
+    int i,oops=0;
+    char line[128];
+
+    if (NULL == (fp = fopen(DISFILE ".c","w"))) {
+	printf("can't open " DISFILE ".c: %s\n",strerror(errno));
+	return;
+    }
+    fprintf(fp,"char oops[]={");
+    for (i=0; i<len; i++) fprintf(fp,"0x%x,",code[i]);
+    fprintf(fp,"};\nmain(){}\n");
+    fclose(fp);
+    
+    system("gcc -c -o " DISFILE ".o " DISFILE ".c");
+    fp = popen("objdump --disassemble-all " DISFILE ".o","r");
+    if (NULL == fp) {
+	printf("can't run objdump: %s\n",strerror(errno));
+	return;
+    }
+    while (NULL != fgets(line,127,fp)) {
+	if (strstr(line,"<oops>"))
+	    oops = 1;
+	if (oops == 1)
+	    printf("code:  %s",line);
+    }
+    printf("\n\n");
+}
+
+void
+oops_decode()
+{
+    char           line[256], klogd[64], *linestart;
+    addr_type      addr;
+    int            len;
+    int            code[40],codelen;
+    
+    while (NULL != fgets(line,255,stdin)) {
+	/* handle syslog timestamp and stuff */
+	if (NULL != (linestart = strstr(line,"kernel: ")))
+	    linestart += 8;
+	else
+	    linestart = line;
+
+	len = 0;
+	if (1 == sscanf(linestart,"EIP: 0010:[<%lx>]%n",&addr,&len) && len) {
+	    printf("EIP:   ");
+	    print_symbol(addr);
+	    printf("\n");
+	}
+	/* klogd was here :-) */
+	if (1 == sscanf(linestart,"EIP: 0010:[%63[^]]]%n",klogd,&len) && len) {
+	    printf("EIP:   %-40s [klogd]\n",klogd);
+	}
+	if (0 == strncmp(linestart,"Call Trace: ",12)) {
+	    linestart += 12;
+	    for (;;) {
+		len = 0;
+		if (1 == sscanf(linestart," [<%lx>] %n",&addr,&len) && len) {
+		    printf("trace: ");
+		    print_symbol(addr);
+		    printf("\n");
+		} else if (1 == sscanf(linestart," [%63[^]]] %n",klogd,&len)
+			   && len) {
+		    printf("trace: %-40s [klogd]\n",klogd);
+		} else
+		    break;
+		linestart += len;
+		if (*linestart == '\0') {
+		    /* printf("DEBUG: call trace linebreak\n"); */
+		    if (NULL == fgets(line,255,stdin))
+			break;
+		    /* handle syslog timestamp and stuff */
+		    if (NULL != (linestart = strstr(line,"kernel: ")))
+			linestart += 8;
+		    else
+			linestart = line;
+		}
+	    }
+	}
+	if (0 == strncmp(linestart,"Code: ",6)) {
+	    linestart += 6;
+	    codelen = 0;
+	    for (;;) {
+		if (1 != sscanf(linestart," %x%n",&code[codelen],&len) || !len)
+		    break;
+		codelen++;
+		linestart += len;
+	    }
+	    disasm(code,codelen);
+	}
+    }
+}
+
+/* --------------------------------------------------------------------- */
+/* main                                                                  */
+
+char *progname;
+char *loadmaps = "/lib/modules/loadmaps";
+
+int proc_modules = 0;
+
+void
+usage(void)
+{
+    fprintf(stderr,
+	    "This is a linux kernel oops decoder\n"
+	    "usage: %s [ options ] mapfile(s) < oops\n"
+	    "options:\n"
+	    "  -h  print this text\n"
+	    "  -m  check /proc/modules and load insmod's maps for the\n"
+	    "      loaded modules from %s\n"
+	    "\n"
+	    "examples:\n"
+	    "oops, system still running\n"
+	    "        dmesg | %s -m /boot/System.map\n"
+	    "oops, system still running, load map for a removed module\n"
+	    "        dmesg | %s -m /boot/System.map \\\n"
+	    "            /lib/modules/loadmaps/removed_module\n"
+	    "decode after reboot\n"
+	    "        tail -n 100 /var/log/messages |\\\n"
+	    "            %s /boot/System.map /lib/modules/loadmaps.last/*\n"
+	    "the hard way: written down by hand:\n"
+	    "        %s /boot/System.map /lib/modules/loadmaps.last/* < log\n"
+	    "Note1: The last two examples assume you have some logic in your\n"
+	    "       /etc/rc* scripts which saves the existing loadmaps before\n"
+	    "       loading modules and/or starting kerneld\n"
+	    "Note2: Use insmod-wrapper to create the loadmaps\n",
+	    progname,loadmaps,
+	    progname,progname,progname,progname);
+}
+
+int
+main(int argc, char **argv)
+{
+    int          c;
+    struct stat  st;
+    
+    if (NULL == (progname = strrchr(argv[0],'/')))
+	progname = argv[0];
+    else
+	progname++;
+
+    /* parse options */
+    for (;;) {
+	if (-1 == (c = getopt(argc, argv, "hm")))
+	    break;
+	switch (c) {
+	case 'm':
+	    proc_modules = 1;
+	    break;
+	case 'h':
+	default:
+	    usage();
+	    exit(1);
+	}
+    }
+
+    if (-1 != fstat(0,&st) && S_ISCHR(st.st_mode)) {
+	fprintf(stderr,
+		"%s expects the oops log from stdin, try -h for help\n",
+		progname);
+	exit(1);
+    }
+
+    /* load symbol files */
+    while (optind < argc)
+	load_file(argv[optind++]);
+    if (proc_modules)
+	load_proc_modules("/proc/modules",loadmaps);
+    printf("\n");
+
+    /* sort symbol files */
+    qsort(symbols,symbolcount,sizeof(struct SYMBOL*),compare_symbols);
+    
+    /* parse stdin */
+    oops_decode();
+
+    return 0;
+}

\
 
 \ /
  Last update: 2005-03-22 13:40    [from the cache]
©2003-2008