From: ag@cogsci.Berkeley.EDU (Keith Gabryelski)
Newsgroups: comp.unix.amiga
Subject: Symbolic Kernel Backtrace Code
Date: 12 Nov 1992 03:34:31 GMT
Organization: Institute of Cognitive Studies, U.C. Berkeley
NNTP-Posting-Host: cogsci.berkeley.edu

I hacked together some kernel backtrace code that will show a
symbolic backtrace with arguments if the kernel panics.

This should make it infinately easier to look at backtraces and
see if they are interesting or not.

The file `syms.c' should go in /usr/sys/amiga/drivers with
an entry placed in cdevsw in master.d/kernel.c.  The node should
be called `/dev/syms'.

The file `stuffsyms' is a shell script that will `nm' /stand/unix
sedding the interesting information and write it to /dev/syms.
This can be run from /etc/sysinit to initialize the kernel symbol
table.

The only trick to getting this stuff to work is to modify the
symbol `backtrace' from /usr/sys/os/exp.  To do this you will need
a binary editor (like emacs) or write your own program.

Change the sequence of characters:

	b a c k t r a c e ^@

in os/exp to:

	B A C K T R A C E ^@

This will modify the symbol `backtrace' so the kernel will use the
one in `syms.c' instead of the one in os/exp.

In emacs you can  accomplish this by edditing /usr/sys/os/exp and
typing:

	^S b a c k t r a c e ^[ ^[ - ^[ u ^X ^S ^X ^C

Pax, Keith

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# This archive contains:
#	syms.c		stuffsyms	
#

echo x - syms.c
cat >syms.c <<'@EOF'
#include "sys/types.h"
#include "sys/file.h"
#include "sys/param.h"
#include "sys/uio.h"
#include "sys/errno.h"
#include "sys/inline.h"
#include "sys/sysmacros.h"
#include "sys/immu.h"
#include "sys/kmem.h"
#include "sys/cred.h"
#include "sys/utsname.h"

static boolean_t symline(char *buf, int length);
static int symsprocess(char *buf, int index);

static boolean_t symsopenned;		/* openned semaphore */
static char *syms;			/* symbol table */
static size_t symslength;		/* length of table */
static size_t symssize;			/* size of alloced memory for table */

int symsopen(dev_t *devp, int mode, int type, cred_t *credp)
{
    int s = spl1();

    if (symsopenned)
    {
	splx(s);
	return EBUSY;
    }

    symsopenned = B_TRUE;

    splx(s);


    return 0;
}

int symsclose(dev_t dev, int mode, cred_t *credp)
{
    symsopenned = B_FALSE;
    return 0;
}

char _symsbuf[1024];
int _symslength;

int symswrite(dev_t dev, uio_t *uiop, cred_t *credp)
{
    while (uiop->uio_resid)
    {
	int error, count, n;

	count = min(sizeof _symsbuf - _symslength, uiop->uio_resid);

	error = uiomove(_symsbuf + _symslength, count, UIO_WRITE, uiop);
	if (error)
	    return error;

	n = symsprocess(_symsbuf, _symslength + count);
	if (n < 0 || n == sizeof _symsbuf)
	{
	    _symslength = 0;
	    if (n < 0)
		return -n;

	    return EINVAL;
	}
	_symslength = n;
    }

    return 0;
}

static boolean_t symline(char *buf, int length)
{
    while (length && *buf != '\n')
    {
	++buf;
	--length;
    }

    return length;
}

static boolean_t symsadd(unsigned long addr, char *symbol, size_t length)
{
    if (!addr || !length)
	return B_TRUE;

    if (symssize - symslength < sizeof addr + length)
    {
	size_t size = symssize + 1024;
	char *ptr;

	ptr = kmem_alloc(size, 0);
	if (!ptr)
	    return B_FALSE;

	if (syms)
	{
	    char *old = syms;

	    bcopy(old, ptr, symslength);
	    syms = ptr;
	    kmem_free(old, symssize);
	}
	else
	    syms = ptr;

	symssize = size;
    }

    *(unsigned long *)&syms[symslength] = addr;
    symslength += sizeof (unsigned long);

    bcopy(symbol, &syms[symslength], length);
    symslength += length;

    return B_TRUE;
}

static int symsprocess(char *buf, int length)
{
    int index;
    unsigned long addr;
    char symbol[1024], *s, *bufp;

    index = 0;
    bufp = buf;
    while (symline(bufp, length - index))
    {
	int c;

	if (*bufp++ != '0' || *bufp++ != 'x')
	    return -EINVAL;

	index += 2;

	addr = 0;
	while ((c = *bufp) != ' ')
	{
	    addr *= 16;
	    if (c < '0' || (c > '9' && c < 'a') || (c > 'f'))
		return -EINVAL;

	    if (c > '9')
		c -= ('a' - 10);
	    else
		c -= '0';
	    addr += c;

	    ++bufp;
	    ++index;
	}
	++bufp;
	++index;

	s = symbol;
	while (*bufp != '\n')
	{
	    *s++ = *bufp++;
	    ++index;
	}
	*s++ = '\0';
	++bufp;
	++index;

	if (!symsadd(addr, symbol, s - symbol))
	    return -ENOMEM;
    }

    length -= index;
    if (length)
	ovbcopy(bufp, buf, length);

    return length;
}

static void strhex(char *buf, long num)
{
    int i;
    int digits[8];

    for (i = 0; i < sizeof digits; i++)
    {
	digits[i] = num % 16;
	num = num / 16;
	if (!num)
	    break;
    }
    if (i == sizeof digits)
	--i;
    *buf++ = '0';
    *buf++ = 'x';
    for (; i >= 0; i--)
	*buf++ = "0123456789abcdef"[digits[i]];
    *buf = '\0';
}

char *findsym(unsigned long addr)
{
    if (addr && syms)
    {
	char *ptr = syms, *lsym = 0;
	unsigned long laddr;

	while (ptr - syms < symslength)
	{
	    if (*(unsigned long *)ptr == addr)
		return ptr + sizeof (unsigned long);

	    if (*(unsigned long *)ptr > addr)
	    {
		if (lsym)
		{
		    for (ptr = _symsbuf; *lsym; )
			*ptr++ = *lsym++;
		    *ptr++ = '+';
		    strhex(ptr, addr - laddr);
		    return _symsbuf;
		}
		else
		    break;
	    }

	    laddr = *(unsigned long *)ptr;
	    ptr += sizeof (unsigned long);
	    lsym = ptr;
	    while (*ptr++)
		;
	}
    }

    strhex(_symsbuf, addr);			       
    return _symsbuf;
}

/*
 * Particular instructions looked for in stack backtrace.
 */
#define JSRAnMASK 0xFFF0
#define JSRAn	 0x4E90		/* jsr (%a0) */
#define CALLMASK 0xFFFF
#define JSRL	 0x4EB9		/* jsr <adr> */
#define JSRLpc	 0x4EBB		/* jsr off.l(%pc) */
#define JSRWpc	 0x4EBA		/* jsr off.w(%pc) */
#define BSRLpc	 0x61FF		/* bsr off.l(%pc) */
#define BSRWpc	 0x6100		/* bsr off.w(%pc) */
#define BSRBMASK 0xFF00
#define BSRB	 0x6100		/* bsr off.b(%pc) */

void backtrace(int dummy)
{
    int *fp;
    short *pc;
    long calladdr = 0;
    static int crashflag;

    if (crashflag++)
	return;

    printf("%s %s Backtrace:\n", utsname.release, utsname.version);

    for (fp = &dummy - 2;
	 printf("%x: ", fp), fp >= (int *)0x40000000 && fp < (int *)0x40010000;
	 printf("(%x, %x, %x)\n",((int*)*fp)[2],((int*)*fp)[3],((int*)*fp)[4]),
	 fp = (int *)*fp)
    {
	pc = (short *)(fp[1]-6);

	if ((*pc & CALLMASK) == JSRL)
	    calladdr = *(long *)(pc+1);
	else if ((*pc & CALLMASK) == JSRLpc || (*pc & CALLMASK) == BSRLpc)
	    calladdr = (long)(pc+1) + *(long *)(pc+1);
	else
	{
	    ++pc;
	    if ((*pc & CALLMASK) == JSRWpc || (*pc & CALLMASK) == BSRWpc)
		calladdr = (long)(pc+1) + pc[1];
	    else
	    {
		++pc;
		if ((*pc & BSRBMASK) == BSRB)
		    calladdr = (long)pc + (signed char)pc[1];
		else
		{
		    if ((*pc & JSRAnMASK) == JSRAn)
			printf("%s->indir", findsym((unsigned long)pc));
		    else
			printf("%s->???", findsym((unsigned long)pc));
		    continue;
		}
	    }
	}

	printf("%s->", findsym((unsigned long)pc));
	printf("%s", findsym(calladdr));
    }
    printf("\n");

#if EXTRA_DEBUG
    {
	extern sde_t cpuroot, userroot;

	printf("Kernel Root Ptr:%x  User Root Ptr:%x\n",
	       cpuroot.wd2.address,userroot.wd2.address);
    }
#endif
}
@EOF

chmod 644 syms.c

echo x - stuffsyms
cat >stuffsyms <<'@EOF'
/usr/ccs/bin/nm -xvph /stand/unix |
/usr/bin/sed -n 's/^\(.*\) . \([a-zA-Z_0-9][a-zA-Z_0-9]*\)$/\1 \2/p' >/dev/syms
@EOF

chmod 755 stuffsyms

exit 0