File:  [Power 6/32 Unix Tahoe 4.2BSD] / cci / usr / src / bin / cdb / single.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Sun Jul 28 12:24:19 2019 UTC (7 years ago) by root
Branches: bsd, MAIN
CVS tags: v12b, v121, HEAD
Power 6/32 Unix version 1.2b

static char sccsid[] = "@(#)single.c	2.8";

#include "cdb.h"


extern char	vsbArgsChild[];	/* defined in pt.c */

export int	vcBadMax; /* most instructions we single step without symbols */

typedef struct {	/* the assertion descriptor */
	char	*sbCheck;	/* points to command list for this check */
	ASE	as;		/* state of THIS assertion check */
} 
ADR, *pADR;
#define cbADR sizeof(ADR)

pADR	vrgAd;	/* the list of assertions */
ADRT	vpcLast;
export int	viadMac, viadMax;
export FLAGT	vfRunAssert; /* see IbpFNewChild */
export ASE	vas;	/* state of the assertions as a whole */



/* A D R   F   S T A C K   F I X */

export ADRT AdrFStackFix(adr)
ADRT	adr;
{
	/* given an address immediately after a procedure call, step over any
	     * stack fix-up instructions
	     */
#ifdef ONYX
	int		inst;
#define INCR15_INST	0xa900
	inst = GetWord(adr, spaceText);
	if ((inst & 0xff00) == INCR15_INST)
		adr += 2;
#endif
#ifdef TAHOE
	/* TAHOE has no stack fix stuff, it's all done by the callee */
#endif
#ifdef VAX
	/* vax has no stack fix stuff, it's all done by the callee */
#endif
#ifdef M68000
	short	inst;

#define TST_INST	0x4a5f
#define TSTL_INST	0x4a9f
#define CMPML_INST	0xbf8f
#define ADDW_INST	0xdefc
#define ADDQ_INST	0x500f
#define ADDN_INST	0x504f

	GetBlock(adr, spaceText, &inst, 2);
	if ( (inst == TST_INST)
	    OR (inst == TSTL_INST)
	    OR (inst == CMPML_INST)
	    OR ((inst & 0xf13f)  == ADDQ_INST))
		adr += 2;
	else if ((inst & 0xf1ff) == ADDN_INST)
		adr += 4;
#endif
	return(adr);
} /* AdrFStackFix */


/* A D R   F   P R E A M B L E */

local ADRT AdrFPreamble(adr)
ADRT	adr;
{
	/* given the first address in a procedure, return the address of the
	     * first `real' instruction - ie. after the csav0 or the brb whatever
	    */
#ifdef ONYX
	int		inst;

#define LDK_INST	0xbd00
#define LD_INST 	0x2100
	inst = GetWord(adr, spaceText) & 0xff00; /* high byte of 1st instruction */
	/* we want to skip over stack set up stuff */
	adr += (inst == LDK_INST) ? 6 : (inst == LD_INST) ? 8 : 4;
#endif
#ifdef TAHOE
	int		inst, specifier;

#define	BRB_INST	0x11	/* Guess they are the only ones after mask */
#define	BRW_INST	0x13
#define JMP_INST	0x71
#define SUBL3_INST	0x3c
	inst = GetByte(adr, spaceText) & 0xff; /* instruction */
	specifier = GetByte((char *)adr + 1, spaceText) & 0xff; /* operand spec */
	/* we want to skip over stack set up stuff */
	switch (inst) {
	case BRB_INST:
		adr += 2; 
		break;
	case BRW_INST:
		adr += 3; 
		break;
	case JMP_INST:
		adr += 6; 
		break;
	case SUBL3_INST:
		if (specifier <= 63) adr += 4 ;
		else switch (specifier) {
		case 0x88: 
			adr += 5; 
			break;
		case 0x89: 
			adr += 6; 
			break;
		case 0x8f: 
			adr += 8; 
			break;
		}
		break;
	default:
		printf("CDB problem - unexpected instruction (0x%0x) at procedure begining\n", inst);
	}
#endif

#ifdef VAX
	int		inst;

#define	BRB_INST	0x11
#define	BRW_INST	0x31
#define JMP_INST	0x17
#define SUBL2_INST	0xc2
	inst = GetByte(adr, spaceText) & 0xff; /* instruction */
	/* we want to skip over stack set up stuff */
	if (inst == BRB_INST)
		adr += 2;
	else if (inst == BRW_INST)
		adr += 3;
	else if (inst == SUBL2_INST)
		adr += 7;
	else 
	    adr += 4;
#endif
#ifdef M68000
	int		ipd, iln, ifd, slop;

	ipd = IpdFAdr(adr);
	if (ipd != ipdNil) {
		IfdLnFAdr(adr, vrgPd[ipd].isym+1, &ifd, &iln, &slop);
#ifdef SUN
		iln++;	/* first line number is connected with preamble */
#endif
		adr = AdrFIfdLn(ifd, iln);
	} /* if */
#endif
	return(adr);
} /* AdrFPreamble */


/* F   A T   C A L L */

local FLAGT FAtCall(adr)
ADRT	adr;
{
	short	inst;
	/* return true or false based on whether the instruction at adr
	     * is a procedure call of any kind.
	     */
	inst = 0;
#ifdef ONYX
#define CALL_INST	0x5f00
#define cbInsMax	2
	GetBlock(adr, spaceText, (ADRT)&inst, cbInsMax);
	return(inst == CALL_INST);
#endif

#ifdef TAHOE
#define	CALLS_INST	0xbf	
#define	CALLG_INST	0xfe
	GetBlock(adr, spaceText, ((char *)&inst)+1, 1);
	return((inst == CALLS_INST) OR (inst == CALLG_INST));
#endif

#ifdef VAX
#define	CALLS_INST	0xfb	/* arguments are on the stack */
#define	CALLG_INST	0xfa	/* arguments are elsewhere */
	GetBlock(adr, spaceText, &inst, 1);
	return((inst == CALLS_INST) OR (inst == CALLG_INST));
#endif

#ifdef M68000
#define JSR_INST	0x4e80
#define BSR_INST	0x6100
#define cbInsMax	2

	GetBlock(adr, spaceText, (ADRT)&inst, cbInsMax);
	return(((inst & 0xff00) == BSR_INST) OR ((inst & 0xffc0) == JSR_INST));
#endif
} /* FAtCall */


/* R E T   F   S P */

local ADRT RetFSp(sp)
ADRT	sp;
{
	/* given the stack pointer immediately after a procedure call,
	     * return the return address
	     */
#ifdef TAHOE
	/* We know that the argument here is realy 'fp', not 'sp'. */
	return(GetWord(sp-8, spaceData)); /* get return */
#endif
#ifdef VAX
	return(GetWord(sp+16, spaceData)); /* get return */
#endif
#ifdef ONYX
	return(GetWord(sp, spaceData)); /* get return */
#endif
#ifdef M68000
	return(GetWord(sp, spaceData)); /* get return */
#endif
} /* RetFSp */


/* R E T   F   F P */

local ADRT RetFFp(fp)
ADRT	fp;
{
	ADRT	ap, pc;
	/* given a frame pointer, return the return address */
	NextFrame(&fp, &ap, &pc);
	return(pc); /* get return */
} /* RetFFp */


/* I B P   F   S I N G L E */

export int IbpFSingle(fBigStep, fQuiet)
FLAGT	fBigStep, fQuiet;
{
	int		cBad, ibp, ifd, ln, slop;
	ADRT	adr;

	if (vpid == pidNil) {
		/* we have no process, create one! */
		IbpFNewChild(vsbArgsChild);
		PrintPos(vpc, (fQuiet) ? fmtNil : fmtProc+fmtLn+fmtPrint);
		return(vibp);
	} /* if */

	/* the idea here is to loop until we get someplace interesting. This
	     * usually means at an address that translates EXACTLY (slop == 0) to
	     * a known line number.
	     */
	cBad = 0;	/* to count instructions in the twilight zone */
	slop = -1; /* just to get us into loop first time */
	while (slop != 0) {
		ibp = ibpNil;
		if (FAtCall(vpc)) { /* at proc call */
			/* follow the call - this way we don't have to deal with N ways
				     * to compute callee's address.
				     */
			ibp = IbpFRun(ptSingle);
			if (ibp != ibpNil)
				return(ibp);	/* a REAL breakpoint */
			if (!fBigStep) {
				if (IpdFAdr(vpc) != ipdNil) {
					adr = AdrFPreamble(vpc); /* allows for intro code */
					ibp = IbpFAdr(adr, 0, sbNil); /* temp BP after intro */
					ibp = IbpFRun(ptResume);
					break; /* go to the stuff at end of this procedure */
				} /* if */
				/* if we get here, then the proc call goes to the
						 * twilight zone - fall into fBigStep code
						 */
			} /* if */
#ifdef TAHOE
			adr = AdrFStackFix(RetFSp(vfp));
#else
			adr = AdrFStackFix(RetFSp(vsp));
#endif
			ibp = IbpFAdr(adr, 0, sbNil); /* temp BP at return */
			ibp = IbpFRun(ptResume);
		} 
		else {	/* NOT at a procedure call */
			if ((ibp = IbpFRun(ptSingle)) != ibpNil)
				return(ibp); /* a REAL breakpoint, so it MUST be kosher! */
			if (IpdFAdr(vpc) != ipdNil) {
				cBad = 0;
			} 
			else {
				/* we are in a file without good symbols */
				/* KLUDGE! There is a problem here.
						 * Some trips to boony land are short, like the switch code
						 * for Onyx, some are MUCH longer, like the printf code.
						 * What we do is go along with the joke for up to
						 * vcBadMax `bad' instructions.  If we don't see daylight, we
						 * set an up level break from the (hopefully good) return
						 * address.  There are NO GUARANTEES that this is correct,
						 * but it seems to work MOST of the time.
						 */
				if (++cBad >= vcBadMax) {
					/* set uplevel break and wait for it to come home */
					adr = RetFFp(vfp); /* get return */
					ibp = IbpFAdr(adr, 0, sbNil);
					ibp = IbpFRun(ptResume);
				} /* if */
				slop = -1;
			} /* if */
		} /* if */
		if ( (ibp != ibpNil) OR (vpid == pidNil) )
			return(ibp);
		if (cBad == 0)
			IfdLnFAdr(vpc, isym0, &ifd, &ln, &slop);
	} /* while */

	if ((vpid != pidNil) AND (ibp == ibpNil))
		PrintPos(vpc, (fQuiet) ? fmtNil : fmtProc+fmtLn+fmtPrint);
	return(ibp);
} /* IbpFSingle */


/* these routines maintain the list of assertions.
 * since the presence of even ONE assertion forces the
 * child process to singlestep, they should only be used
 * when your back is against the wall e.g. someone is stepping
 * on a global and you don't know who!
 */


/* I N I T   A S S E R T */

export void InitAssert()
{
	viadMac = 0;
	vrgAd = (pADR) malloc(viadMax * cbADR);
	vcbTot += viadMax * cbADR;
	vas = asActive;
} /* InitAssert */


/* L I S T   A S S E R T */

export void ListAssert()
{
	int		i;

	if (viadMac == 0)
		UError("No assertions");
	printf("Assertions in general are %s\n\n",
	(vas == asActive) ? "ACTIVE" : "SUSPENDED");
	for (i=0; i < viadMac; i++) {
		printf("%2d: %s %s\n", i,
		(vrgAd[i].as==asActive) ? "Active" : "Suspended", vrgAd[i].sbCheck);
	} /* for */
} /* ListAssert */


/* A D D   A S S E R T */

export void AddAssert(sbCheck)
char	*sbCheck;
{
	int		cb, iad;

	if (viadMac >= viadMax)
		UError("Too many assertions");
	iad = viadMac++;
	cb = strlen(sbCheck);
	vrgAd[iad].sbCheck = malloc(cb+1); /* room for null */
	vcbTot += cb + 1;
	strcpy(vrgAd[iad].sbCheck, sbCheck);
	vrgAd[iad].sbCheck[cb] = chNull;
	vrgAd[iad].as = asActive;
	printf("Assertion %d: Active \"%s\"\n", iad, vrgAd[iad].sbCheck);
	vas = asActive;
	printf("Assertions are ACTIVE");
} /* AddAssert */


/* M O D   A S S E R T */

export void ModAssert(iad, as)
int	iad;
ASE	as;
{
	if (iad < 0 OR iad >= viadMac)
		UError("Bad assertion number: %d", iad);
	vrgAd[iad].as = as;
	if (as == asNil) {
		free(vrgAd[iad].sbCheck);
		vrgAd[iad] = vrgAd[viadMac-1];	/* fold it back on itself */
		viadMac--;
	}
	if (vas == asSuspended)
		return;
	for (iad = 0; iad < viadMac; iad++)
		if (vrgAd[iad].as == asActive)
			return;
	printf("Assertions are ACTIVE");
	vas = asSuspended;
} /* ModAssert */


/* F   D O   A S S E R T */

export void FDoAssert(pt)
int	pt;
{
	int		iad;
	long	cnt;

	vfRunAssert = false;  /* so we are not recursive entered from IbpFRun */
	PushCmd(vsbCmd);	/* save what we are doing */
	for (cnt=(pt==ptSingle) ? 1 : -1;cnt != 0; --cnt) {
		/* we do the assertions BEFORE the line is executed */
		for(iad=0; iad < viadMac; iad++) {
			if ((vrgAd[iad].as == asActive)
			    AND (FDoCommand(vrgAd[iad].sbCheck, false))) {
				/* we hit an 'x' (exit) command - stop the parade! */
				printf("\nHit on assertion %d: \"%s\"\n",
				iad, vrgAd[iad].sbCheck);
				printf("Last line executed was:\n");
				PrintPos(vpcLast, fmtFile+fmtProc+fmtLn+fmtPrint+fmtSave);
				printf("Next line to execute is:\n");
				PrintPos(vpc, fmtFile+fmtProc+fmtLn+fmtPrint);
				vfRunAssert = true;
				return(true);
			} /* if */
		} /* for */
		vpcLast = vpc;	/* remember where the current line is */
		/* step the process one statement's worth */
		if (ibpNil != IbpFSingle(false, true)) {
			vfRunAssert = true;
			return(false);
		} /* if */
	} /* for */
	PrintPos(vpc, fmtNil);	/* restore our context */
	vfRunAssert = true;
	return(false);
} /* FDoAssert */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.