|
|
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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.