/* program for testing stack unwinding.
 *
 * DP Peter April 1994
 *
 * compile using:
 *
 *	cc cfile.c dfile.c sfile.s -o cfile -Wl,-a,archive -lcl -Ae -g; chmod +x cfile
 * 
 * sccs id:	@(#)cfile.c	1.11 04/21/94
 */

#include <stdio.h>
#include <memory.h>

typedef struct current_frame_def {
	unsigned cur_frsize;
	unsigned cursp;
	unsigned currls;
	unsigned currlo;
	unsigned curdp;
	unsigned toprp;
	unsigned topmrp;
	unsigned topsr0;
	unsigned topsr4;
	unsigned r3;
	unsigned cur_r19;
} UWREC; 

typedef struct utable {
	unsigned unwind_table_start;
	unsigned unwind_table_end;
} UTABLE;

/* externals */
extern int dcall();
extern int wrapc();
extern int get_cspace();
extern int get_dspace();
extern int get_space();
extern int *gen_ocall();
extern UTABLE U_get_unwind_table();
extern int U_get_unwind_entry();
extern int U_get_previous_frame();

/* forwards */
int display_stack_trace();
int icall();
int main();
int gen_stack_trace();
int get_NextFrame();

display_stack_trace(int dummy)
{
	char str[1024];

	sprintf(str, "\n");
	gen_stack_trace(str);
	printf("%s\n",str);
}

icall(int (*fptr)())
{
	(*fptr)();
}

char array[1024];

int *m_ptr;
int *s_ptr;
int *e_ptr;

main(int argc, char **argv)
{
	unsigned cspace = get_cspace();
	unsigned dspace = get_dspace();
	UTABLE utab = U_get_unwind_table(0);
	int stat;
	int (*dptr)() = &dcall;
	int (*iptr)() = &icall;
	int (*optr)();

	/* table for functions to display unwind info for */
	static int (*ftable[])() = {get_cspace, get_dspace, get_space, wrapc, display_stack_trace, 
					main, gen_stack_trace, get_NextFrame, dcall, icall, 0};
	static char *fname[] = {"get_cspace", "get_dspace", "get_space", "wrapc", "display_stack_trace", 
					"main", "gen_stack_trace", "get_NextFrame", "dcall", "icall", 0};
	int index;

	/* set up pointers to uninitialised and malloc'd data areas */
	m_ptr = (int *)malloc(1024);
	s_ptr = (int *)array;

	/* display the code and data spaces */
	printf("code space 0x%08x\n",cspace);
	printf("data space 0x%08x\n",dspace);

	/* display the unwind table location */
	printf("unwind table [0x%08x...0x%08x]\n", utab.unwind_table_start, utab.unwind_table_end);

	for (index = 0; fname[index] != 0; index++) {

		int *p_start, *p_end, *callinfo_1, *callinfo_2;

		/* display the function address */
		printf("Function '%s' @ %08x.%08x\n", fname[index], get_space(ftable[index]), ftable[index]);

		/* get the unwind table entry for the function */
		stat = U_get_unwind_entry(ftable[index], cspace, utab.unwind_table_start, utab.unwind_table_end);
		printf("\tunwind table offset for '%s' @ 0x%08x\n", fname[index], stat);

		/* unwind descriptors consist of 4-word entries: .PROC (1 word), .PROCEND (1 word) and .CALLINFO (2 words) */
		p_start = (int *)stat, p_end = (int *)(stat + 4), callinfo_1 = (int *)(stat + 8), callinfo_2 = (int *)(stat + 12);
		printf("\tunwind table data for '%s'\n", fname[index]);
		printf("\t\t.PROC        0x%08x\n", *p_start);
		printf("\t\t.PROCEND     0x%08x\n", *p_end);
		printf("\t\t.CALLINFO_1  0x%08x\n", *callinfo_1);
		printf("\t\t.CALLINFO_2  0x%08x\n", *callinfo_2);
	}
	printf("\n");

	/* see what we get for something malloc'd and something static */
	printf("Data '%s' @ %08x.%08x\n", "static", get_space(s_ptr), s_ptr);
	stat = U_get_unwind_entry(s_ptr, cspace, utab.unwind_table_start, utab.unwind_table_end);
	printf("\tunwind table offset for '%s' @ 0x%08x\n", "static", stat);

	printf("Data '%s' @ %08x.%08x\n", "malloc", get_space(m_ptr), m_ptr);
	stat = U_get_unwind_entry(m_ptr, cspace, utab.unwind_table_start, utab.unwind_table_end);
	printf("\tunwind table offset for '%s' @ 0x%08x\n", "malloc", stat);

	printf("\n");

	/* generate a version of 'dcall' in malloc'd data space and point optr at it so 
	that it can be called. */
	e_ptr = gen_ocall(m_ptr);
	optr = (int (*)(int))m_ptr;

	printf("Function '%s' starts @ %08x.%08x, ends @ %08x.%08x\n", "ocall",
			get_space(m_ptr), m_ptr, get_space(e_ptr), e_ptr);

	/* generate a dynamic unwind descriptor for 'ocall' but in the static space */
	*(s_ptr+0) = (int)m_ptr;
	*(s_ptr+1) = (int)e_ptr;
	*(s_ptr+2) = 0x00000008;
	*(s_ptr+3) = 0x00000008;

	printf("\n");

	/* finally do the stack trace using all the indirections we have */
	(*iptr)(&display_stack_trace);
	(*dptr)(&display_stack_trace);
	(*optr)(&display_stack_trace);
}

gen_stack_trace(outstr)
char *outstr;
{
	unsigned sp, pc, rp;
	UWREC rec1;
	int depth;

	sp = (unsigned)(&outstr + 9);
	pc = (* (int *) (sp - 20));
	rp = 0;

	rec1.currls = get_cspace();
	rec1.cursp = sp;
	rec1.currlo = pc;
	rec1.toprp = rp;
	rec1.curdp = 0;
	rec1.cur_frsize = 0;

	for (depth = 0; rec1.currlo; depth++) {
		sprintf(outstr, "%s (%2d) pc=0x%08x.0x%08x sr=0x%08x dp=0x%08x\n", outstr, depth, 
				get_space(rec1.currlo), rec1.currlo, rec1.currls, rec1.curdp);
		if (get_NextFrame(&rec1) == -1) {
			return;
		}
	}
	U_STACK_TRACE();
}


int get_NextFrame(prec1)
UWREC *prec1;
{
	int stat;
	UWREC rec2;

	stat = U_get_previous_frame(prec1, &rec2);
	if (stat) {
		fprintf(stderr, "Stack_Trace: error (%d) while unwinding stack\n", stat);
		return (-1);
	}

	prec1->currlo		= rec2.currlo;
	prec1->cur_frsize	= rec2.cur_frsize;
	prec1->cursp		= rec2.cursp;
	prec1->currls		= rec2.currls;
	prec1->curdp		= rec2.curdp;

	return 0;
}
