Provide NEdit Macro stack traces Macro function names are listed when a crash occurs in one of your macros. Debug tracing enhanced to show symbol values in stack traces listed to terminal output: a boon to interpret.c hackers. Try changing the definition #define STACKDUMP(n, x) stackdump(n, x) to #define STACKDUMP(n, x) stackdump(n, x + 50) and watching the output of NEdit in an xterm generated while running your favorite macros! (You will need to add -DDEBUG_STACK and -DDEBUG_ASSEMBLY in your compilation flags to enable the debug tracing.) Thanks to Eddy De Greef! diff -ur -b nedit_official nedit_mod diff -ur -b nedit_official/source/interpret.c nedit_mod/source/interpret.c --- nedit_official/source/interpret.c 2004-05-12 05:21:40.000000000 -0400 +++ nedit_mod/source/interpret.c 2004-05-18 00:37:40.181772000 -0400 @@ -38,6 +38,7 @@ #include "rbTree.h" #include +#include #include #include #include @@ -140,7 +141,10 @@ #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) #define DEBUG_DISASSEMBLER +static const char *printd(const char *f, ...); +static int outPrintd(); static void disasm(Inst *inst, int nInstr); +static void disasmInternal(Inst *inst, int nInstr); #endif /* #if defined(DEBUG_ASSEMBLY) || defined(DEBUG_STACK) */ #ifdef DEBUG_ASSEMBLY /* for disassembly */ @@ -151,6 +155,7 @@ #ifdef DEBUG_STACK /* for run-time instruction and stack trace */ static void stackdump(int n, int extra); +static void stackdumpInternal(int n, int extra); #define STACKDUMP(n, x) stackdump(n, x) #define DISASM_RT(i, n) disasm(i, n) #else /* #ifndef DEBUG_STACK */ @@ -210,13 +215,17 @@ arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray}; /* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */ -#define FP_ARG_ARRAY_CACHE_INDEX (-1) +#define FP_ARG_ARRAY_INDEX (-1) #define FP_ARG_COUNT_INDEX (-2) -#define FP_OLD_FP_INDEX (-3) -#define FP_RET_PC_INDEX (-4) -#define FP_TO_ARGS_DIST (4) /* should be 0 - (above index) */ +#define FP_FUNCTION_NAME (-3) /* !! */ +#define FP_SYMBOL_TABLE (-4) /* !! */ +#define FP_OLD_FP_INDEX (-5) +#define FP_RET_PC_INDEX (-6) + +#define FP_TO_ARGS_DIST (0 - FP_RET_PC_INDEX) /* should be 0 - (above index) */ + #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex)) -#define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_CACHE_INDEX)) +#define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_INDEX)) #define FP_GET_ARG_COUNT(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_COUNT_INDEX).val.n) #define FP_GET_OLD_FP(xFrameP) ((FP_GET_ITEM(xFrameP, FP_OLD_FP_INDEX)).val.dataval) #define FP_GET_RET_PC(xFrameP) ((FP_GET_ITEM(xFrameP, FP_RET_PC_INDEX)).val.inst) @@ -489,6 +498,15 @@ *(context->stackP++) = noValue; /* old FrameP */ + context->stackP->tag = NO_TAG; + context->stackP->val.sym = prog->localSymList; /* symbol table */ + context->stackP++; + + context->stackP->tag = STRING_TAG; + context->stackP->val.str.rep = ""; /* function name */ + context->stackP->val.str.len = strlen(context->stackP->val.str.rep); + context->stackP++; + context->stackP->tag = NO_TAG; /* nArgs */ context->stackP->val.n = nArgs; context->stackP++; @@ -588,6 +606,15 @@ StackP->val.dataval = FrameP; /* old FrameP */ StackP++; + StackP->tag = NO_TAG; + StackP->val.sym = prog->localSymList; /* symbol table */ + StackP++; + + StackP->tag = STRING_TAG; + StackP->val.str.rep = ""; /* function name */ + StackP->val.str.len = strlen(StackP->val.str.rep); + StackP++; + StackP->tag = NO_TAG; /* nArgs */ StackP->val.n = 0; StackP++; @@ -1908,6 +1935,8 @@ ** values which are already there. */ if (sym->type == MACRO_FUNCTION_SYM) { + prog = (Program *)sym->value.val.str.rep; + StackP->tag = NO_TAG; /* return PC */ StackP->val.inst = PC; StackP++; @@ -1916,6 +1945,15 @@ StackP->val.dataval = FrameP; StackP++; + StackP->tag = NO_TAG; + StackP->val.sym = prog->localSymList; /* symbol table */ + StackP++; + + StackP->tag = STRING_TAG; + StackP->val.str.rep = sym->name; /* function name */ + StackP->val.str.len = strlen(sym->name); + StackP++; + StackP->tag = NO_TAG; /* nArgs */ StackP->val.n = nArgs; StackP++; @@ -1923,7 +1961,6 @@ *(StackP++) = noValue; /* cached arg array */ FrameP = StackP; - prog = (Program *)sym->value.val.str.rep; PC = prog->code; for (s = prog->localSymList; s != NULL; s = s->next) { FP_GET_SYM_VAL(FrameP, s) = noValue; @@ -2423,7 +2460,7 @@ PC++; DISASM_RT(PC-2, 2); - STACKDUMP(nDim, 3); + STACKDUMP(nDim+1, 3); if (nDim > 0) { errNum = makeArrayKeyFromArgs(nDim, &keyString, 0); @@ -2474,7 +2511,7 @@ PC++; DISASM_RT(PC-2, 1); - STACKDUMP(nDim, 3); + STACKDUMP(nDim+2, 3); if (nDim > 0) { POP(srcValue) @@ -2512,9 +2549,9 @@ ** for use with assign-op operators (eg a[i,j] += k ** ** Before: Prog-> [binOp], nDim, next, ... -** Stack-> [rhs], indnDim, ... ind1, next, ... +** Stack-> [rhs], indnDim, ... ind1, ArraySym, next, ... ** After: Prog-> binOp, nDim, [next], ... -** Stack-> [rhs], arrayValue, next, ... +** Stack-> [rhs], arrayValue, ArraySym, next, ... */ static int arrayRefAndAssignSetup(void) { @@ -2529,7 +2566,7 @@ PC++; DISASM_RT(PC-3, 3); - STACKDUMP(nDim + 1, 3); + STACKDUMP(nDim + (binaryOp ? 2 : 1), 3); if (binaryOp) { POP(moveExpr) @@ -2783,6 +2820,59 @@ } /* +** build a stack dump string, reallocating s as necessary. +*/ +static char *stackDumpStr(DataValue *fp, const char *msg, char **s, int *pLen) +{ + int len; + const char *op; + char *np; + DataValue *nfp = fp; + +#ifdef DEBUG_STACK + const char *dump; + printd("\n\n"); + disasmInternal(PC - 1, 1); + stackdumpInternal(0, 50); + dump = printd(NULL); +#endif + + /* first measure the lengths */ + len = strlen(msg) + 1; + for (nfp = fp; nfp; nfp = FP_GET_OLD_FP(nfp)) { + len = len + FP_GET_ITEM(nfp, FP_FUNCTION_NAME).val.str.len + 1; + } +#ifdef DEBUG_STACK + len += strlen(dump); +#endif + if (*pLen < len) + { + *s = *s ? XtRealloc(*s, len) : XtMalloc(len); + *pLen = len; + } + /* now copy */ + np = *s; + op = msg; + while (*op) + *np++ = *op++; + + for (nfp = fp; nfp; nfp = FP_GET_OLD_FP(nfp)) { + *np++ = '\n'; + op = FP_GET_ITEM(nfp, FP_FUNCTION_NAME).val.str.rep; + while (*op) + *np++ = *op++; + } +#ifdef DEBUG_STACK + op = dump; + while (*op) + *np++ = *op++; +#endif + + *np = 0; + return *s; +} + +/* ** combine two strings in a static area and set ErrMsg to point to the ** result. Returns false so a single return execError() statement can ** be used to both process the message and return. @@ -2790,9 +2880,11 @@ static int execError(const char *s1, const char *s2) { static char msg[MAX_ERR_MSG_LEN]; + static char *err = NULL; + static int errlen = 0; sprintf(msg, s1, s2); - ErrMsg = msg; + ErrMsg = stackDumpStr(FrameP, msg, &err, &errlen); return STAT_ERROR; } @@ -2826,11 +2918,83 @@ } #ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */ +static char *printdBuffer = NULL; +static int printdPos = 0; +static int printdSize = 0; + +static const char *printd(const char *f, ...) +{ + char buffer[4096]; + int len; + va_list args; + + if (!f) + { + printdPos = 0; /* reset for next time */ + return printdBuffer; + } + + va_start(args, f); + vsprintf(buffer, f, args); + va_end(args); + + len = strlen(buffer); + if (!printdBuffer) + { + printdSize = 4096; + printdBuffer = XtMalloc(printdSize); + printdPos = 0; + } + else + { + int needSize = printdPos + len + 1; + if (needSize > printdSize) + { + int newSize = printdSize; + while (newSize < needSize) + newSize *= 2; + printdBuffer = XtRealloc(printdBuffer, newSize); + printdSize = newSize; + } + } + strcpy(&printdBuffer[printdPos], buffer); + printdPos += len; + + return printdBuffer; +} + +int outPrintd() +{ + const char *s = printd(NULL); + const char *cp; + + static int outIsTTY = -1; + if (outIsTTY == -1) + outIsTTY = isatty(fileno(stdout)); + + if (outIsTTY) + { + for (cp = s; *cp; cp++) + if (*cp == '\n') + printf("\033[K\n"); + else + putchar(*cp); + } + else + { + for (cp = s; *cp; cp++) + putchar(*cp); + } + return cp - s; +} +#endif /* #ifdef DEBUG_DISASSEMBLER */ + +#ifdef DEBUG_DISASSEMBLER /* dumping values in disassembly or stack dump */ static void dumpVal(DataValue dv) { switch (dv.tag) { case INT_TAG: - printf("i=%d", dv.val.n); + printd("i=%d", dv.val.n); break; case STRING_TAG: { @@ -2838,38 +3002,38 @@ char s[21]; char *src = dv.val.str.rep; if (!src) { - printf("s="); + printd("s="); } else { for (k = 0; src[k] && k < sizeof s - 1; k++) { s[k] = isprint(src[k]) ? src[k] : '?'; } s[k] = 0; - printf("s=\"%s\"%s[%d]", s, + printd("s=\"%s\"%s[%d]", s, src[k] ? "..." : "", strlen(src)); } } break; case ARRAY_TAG: - printf(""); + printd("%08p [%d]", dv.val.arrayPtr, ArraySize(&dv)); break; case NO_TAG: if (!dv.val.inst) { - printf(""); + printd(""); } else { - printf("?%8p", dv.val.inst); + printd("?%8p", dv.val.inst); } break; default: - printf("UNKNOWN DATA TAG %d ?%8p", dv.tag, dv.val.inst); + printd("UNKNOWN DATA TAG %d ?%8p", dv.tag, dv.val.inst); break; } } #endif /* #ifdef DEBUG_DISASSEMBLER */ #ifdef DEBUG_DISASSEMBLER /* For debugging code generation */ -static void disasm(Inst *inst, int nInstr) +static void disasmInternal(Inst *inst, int nInstr) { static const char *opNames[N_OPS] = { "RETURN_NO_VAL", /* returnNoVal */ @@ -2918,39 +3082,40 @@ }; int i, j; - printf("\n"); + printd("\n"); for (i = 0; i < nInstr; ++i) { - printf("Prog %8p ", &inst[i]); + printd("Prog %8p ", &inst[i]); for (j = 0; j < N_OPS; ++j) { if (inst[i] == OpFns[j]) { - printf("%22s ", opNames[j]); + printd("%22s ", opNames[j]); if (j == OP_PUSH_SYM || j == OP_ASSIGN) { Symbol *sym = (Symbol *)inst[i+1]; - printf("%s", sym->name); + printd("%s", sym->name); if (sym->value.tag == STRING_TAG && strncmp(sym->name, "string #", 8) == 0) { + printd(" "); dumpVal(sym->value); } ++i; } else if (j == OP_BRANCH || j == OP_BRANCH_FALSE || j == OP_BRANCH_NEVER || j == OP_BRANCH_TRUE) { - printf("to=(%d) %x", (int)inst[i+1], + printd("to=(%d) %x", (int)inst[i+1], (int)(&inst[i+1] + (int)inst[i+1])); ++i; } else if (j == OP_SUBR_CALL) { - printf("%s (%d arg)", ((Symbol *)inst[i+1])->name, + printd("%s (%d arg)", ((Symbol *)inst[i+1])->name, (int)inst[i+2]); i += 2; } else if (j == OP_BEGIN_ARRAY_ITER) { - printf("%s in", + printd("%s in", ((Symbol *)inst[i+1])->name); ++i; } else if (j == OP_ARRAY_ITER) { - printf("%s = %s++ end-loop=(%d) %x", + printd("%s = %s++ end-loop=(%d) %x", ((Symbol *)inst[i+1])->name, ((Symbol *)inst[i+2])->name, (int)inst[i+3], @@ -2959,70 +3124,184 @@ } else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE || j == OP_ARRAY_ASSIGN) { - printf("nDim=%d", + printd("nDim=%d", ((int)inst[i+1])); ++i; } else if (j == OP_ARRAY_REF_ASSIGN_SETUP) { - printf("binOp=%s ", + printd("binOp=%s ", ((int)inst[i+1]) ? "true" : "false"); - printf("nDim=%d", + printd("nDim=%d", ((int)inst[i+2])); i += 2; } else if (j == OP_PUSH_ARRAY_SYM) { - printf("%s", ((Symbol *)inst[++i])->name); - printf(" %s", + printd("%s", ((Symbol *)inst[++i])->name); + printd(" %s", (int)inst[i+1] ? "createAndRef" : "refOnly"); ++i; } - printf("\n"); + printd("\n"); break; } } if (j == N_OPS) { - printf("%x\n", (int)inst[i]); + printd("%x\n", (int)inst[i]); } } } + +static void disasm(Inst *inst, int nInstr) +{ + static int outIsTTY = -1; + if (outIsTTY == -1) outIsTTY = isatty(fileno(stdout)); + if (outIsTTY) { printd("\033[H"); } + disasmInternal(inst, nInstr); + outPrintd(); +} #endif /* #ifdef DEBUG_DISASSEMBLER */ #ifdef DEBUG_STACK /* for run-time stack dumping */ #define STACK_DUMP_ARG_PREFIX "Arg" -static void stackdump(int n, int extra) +static void stackdumpframe(DataValue *arrow, DataValue *outpt, DataValue *fp, + DataValue *sp, char topMark) { - /* Stack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */ - int nArgs = FP_GET_ARG_COUNT(FrameP); - int i, offset; - char buffer[sizeof(STACK_DUMP_ARG_PREFIX) + TYPE_INT_STR_SIZE(int)]; - printf("Stack ----->\n"); - for (i = 0; i < n + extra; i++) { - char *pos = ""; - DataValue *dv = &StackP[-i - 1]; - if (dv < Stack) { - printf("--------------Stack base--------------\n"); - break; + DataValue *baseF = &FP_GET_ITEM(fp, FP_OLD_FP_INDEX); + DataValue *oldFP = baseF ? baseF->val.dataval : NULL; + DataValue *arg1 = &FP_GET_ARG_N(fp, 0); + DataValue *fnNm = &FP_GET_ITEM(fp, FP_FUNCTION_NAME); + DataValue *dv; + DataValue *endDv = (arg1 > outpt) ? arg1 : outpt; + int nArgs = FP_GET_ARG_COUNT(fp); + + int nSyms; + static int symLen = 0; + Symbol *syms = FP_GET_ITEM(fp, FP_SYMBOL_TABLE).val.sym; + Symbol *sym; + +#ifdef DEBUG_STACK_HEADFIRST +#else + /* do caller's frame */ + if (oldFP || arg1 > endDv) + stackdumpframe(arrow, outpt, oldFP, arg1, ' '); +#endif /* #ifdef DEBUG_STACK_HEADFIRST */ + + /* do current frame */ + /* how many symbols are there? */ + for (sym = syms, nSyms = 0; sym != NULL; sym = sym->next) { + nSyms++; + if (symLen < 27) { + int len = strlen(sym->name); + if (len > 27) + len = 27; + if (len > symLen) + symLen = len; + } } - offset = dv - FrameP; - printf("%4.4s", i < n ? ">>>>" : ""); - printf("%8p ", dv); + /* output instructions between endDv and sp - 1 inclusive */ +#ifdef DEBUG_STACK_HEADFIRST + dv = sp; + while (--dv >= endDv) +#else + for (dv = endDv; dv < sp; dv++) +#endif /* #ifdef DEBUG_STACK_HEADFIRST */ + { + const char *posFmt = "%-6s "; + const char *symName = ""; + + char *pos = ""; + char buffer[sizeof(STACK_DUMP_ARG_PREFIX) + TYPE_INT_STR_SIZE(int)]; + int offset = dv - fp; + const char *leadIn = (dv >= arrow) ? ">>>>" : + (dv == arg1) ? "----" : + (dv == fnNm) ? "====" : ""; + printd("%4.4s", leadIn); + printd("%8p%c", dv, topMark); switch (offset) { - case 0: pos = "FrameP"; break; /* first local symbol value */ - case FP_ARG_ARRAY_CACHE_INDEX: pos = "args"; break; /* number of arguments */ + case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* arguments as array */ case FP_ARG_COUNT_INDEX: pos = "NArgs"; break; /* number of arguments */ + case FP_FUNCTION_NAME: pos = "FnName"; break; + case FP_SYMBOL_TABLE: pos = "FnSyms"; break; case FP_OLD_FP_INDEX: pos = "OldFP"; break; case FP_RET_PC_INDEX: pos = "RetPC"; break; default: - if (offset < -FP_TO_ARGS_DIST && offset >= -FP_TO_ARGS_DIST - nArgs) { - sprintf(pos = buffer, STACK_DUMP_ARG_PREFIX "%d", offset + FP_TO_ARGS_DIST + nArgs + 1); + if (offset < -FP_TO_ARGS_DIST && + offset >= -FP_TO_ARGS_DIST - nArgs) + { + sprintf(pos = buffer, STACK_DUMP_ARG_PREFIX "%d", + offset + FP_TO_ARGS_DIST + nArgs + 1); + } + else if (0 <= offset && offset < nSyms) { + sprintf(pos = buffer, offset ? "[%d]" : "FP[%d]", offset); + posFmt = "%6s "; + } + else if (offset == 0) { + pos = "FrameP"; } break; } - printf("%-6s ", pos); + printd(posFmt, pos); + + /* local symbol names? */ + if (offset < nSyms) { + for (sym = syms; sym != NULL; sym = sym->next) { + if (sym->value.val.n == offset) { + symName = sym->name; + break; + } + } + } + printd("%-*.*s ", symLen, symLen, symName); + + if (dv == fnNm && dv->tag == STRING_TAG && dv->val.str.rep) + printd("%s", dv->val.str.rep); + else dumpVal(*dv); - printf("\n"); + + printd("\n"); } + +#ifdef DEBUG_STACK_HEADFIRST + /* do caller's frame */ + if (oldFP || arg1 > endDv) + stackdumpframe(arrow, outpt, oldFP, arg1, ' '); +#else +#endif /* #ifdef DEBUG_STACK_HEADFIRST */ } + +static void stackdumpInternal(int n, int extra) +{ + DataValue *arrow = StackP - n; + DataValue *outpt = StackP - n - extra; + +#ifdef DEBUG_STACK_HEADFIRST + printd("Stack ----->\n"); + stackdumpframe(arrow, outpt, FrameP, StackP, '*'); + if (outpt < Stack) + printd("--------------Stack base--------------\n"); +#else + if (outpt < Stack) + printd("--------------Stack base--------------\n"); + stackdumpframe(arrow, outpt, FrameP, StackP, '*'); + printd("Stack ----->\n"); +#endif /* #ifdef DEBUG_STACK_HEADFIRST */ +} + +static void stackdump(int n, int extra) +{ + static int outIsTTY = -1; + if (outIsTTY == -1) + outIsTTY = isatty(fileno(stdout)); + + stackdumpInternal(n, extra); + + if (outIsTTY) + printd("\033[J\n"); + + outPrintd(); + fflush(stdout); +} + #endif /* ifdef DEBUG_STACK */ diff -ur -b nedit_official/source/interpret.h nedit_mod/source/interpret.h --- nedit_official/source/interpret.h 2004-04-30 10:35:16.000000000 -0400 +++ nedit_mod/source/interpret.h 2004-05-18 00:37:40.251768000 -0400 @@ -55,6 +55,7 @@ Inst* inst; struct DataValueTag* dataval; struct SparseArrayEntry *arrayPtr; + struct SymbolRec *sym; } val; } DataValue;