diff options
Diffstat (limited to 'lib/system/endb.nim')
-rw-r--r-- | lib/system/endb.nim | 558 |
1 files changed, 0 insertions, 558 deletions
diff --git a/lib/system/endb.nim b/lib/system/endb.nim deleted file mode 100644 index b2cc5624b..000000000 --- a/lib/system/endb.nim +++ /dev/null @@ -1,558 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This file implements the embedded debugger that can be linked -# with the application. Mostly we do not use dynamic memory here as that -# would interfere with the GC and trigger ON/OFF errors if the -# user program corrupts memory. Unfortunately, for dispaying -# variables we use the ``system.repr()`` proc which uses Nim -# strings and thus allocates memory from the heap. Pity, but -# I do not want to implement ``repr()`` twice. - -const - EndbBeg = "*** endb" - EndbEnd = "***\n" - -type - StaticStr = object - len: int - data: array[0..100, char] - - BreakpointFilename = object - b: ptr Breakpoint - filename: StaticStr - - DbgState = enum - dbOff, # debugger is turned off - dbStepInto, # debugger is in tracing mode - dbStepOver, - dbSkipCurrent, - dbQuiting, # debugger wants to quit - dbBreakpoints # debugger is only interested in breakpoints -{.deprecated: [TStaticStr: StaticStr, TBreakpointFilename: BreakpointFilename, - TDbgState: DbgState].} - -var - dbgUser: StaticStr # buffer for user input; first command is ``step_into`` - # needs to be global cause we store the last command - # in it - dbgState: DbgState # state of debugger - dbgSkipToFrame: PFrame # frame to be skipped to - - maxDisplayRecDepth: int = 5 # do not display too much data! - - brkPoints: array[0..127, BreakpointFilename] - -proc setLen(s: var StaticStr, newLen=0) = - s.len = newLen - s.data[newLen] = '\0' - -proc add(s: var StaticStr, c: char) = - if s.len < high(s.data)-1: - s.data[s.len] = c - s.data[s.len+1] = '\0' - inc s.len - -proc add(s: var StaticStr, c: cstring) = - var i = 0 - while c[i] != '\0': - add s, c[i] - inc i - -proc assign(s: var StaticStr, c: cstring) = - setLen(s) - add s, c - -proc `==`(a, b: StaticStr): bool = - if a.len == b.len: - for i in 0 .. a.len-1: - if a.data[i] != b.data[i]: return false - return true - -proc `==`(a: StaticStr, b: cstring): bool = - result = c_strcmp(a.data, b) == 0 - -proc write(f: File, s: StaticStr) = - write(f, cstring(s.data)) - -proc listBreakPoints() = - write(stdout, EndbBeg) - write(stdout, "| Breakpoints:\n") - for b in listBreakpoints(): - write(stdout, abs(b.low)) - if b.high != b.low: - write(stdout, "..") - write(stdout, abs(b.high)) - write(stdout, " ") - write(stdout, b.filename) - if b.isActive: - write(stdout, " [disabled]\n") - else: - write(stdout, "\n") - write(stdout, EndbEnd) - -proc openAppend(filename: cstring): File = - var p: pointer = fopen(filename, "ab") - if p != nil: - result = cast[File](p) - write(result, "----------------------------------------\n") - -proc dbgRepr(p: pointer, typ: PNimType): string = - var cl: ReprClosure - initReprClosure(cl) - cl.recDepth = maxDisplayRecDepth - # locks for the GC turned out to be a bad idea... - # inc(recGcLock) - result = "" - reprAux(result, p, typ, cl) - # dec(recGcLock) - deinitReprClosure(cl) - -proc writeVariable(stream: File, slot: VarSlot) = - write(stream, slot.name) - write(stream, " = ") - writeLine(stream, dbgRepr(slot.address, slot.typ)) - -proc listFrame(stream: File, f: PFrame) = - write(stream, EndbBeg) - write(stream, "| Frame (") - write(stream, f.len) - write(stream, " slots):\n") - for i in 0 .. f.len-1: - writeLine(stream, getLocal(f, i).name) - write(stream, EndbEnd) - -proc listLocals(stream: File, f: PFrame) = - write(stream, EndbBeg) - write(stream, "| Frame (") - write(stream, f.len) - write(stream, " slots):\n") - for i in 0 .. f.len-1: - writeVariable(stream, getLocal(f, i)) - write(stream, EndbEnd) - -proc listGlobals(stream: File) = - write(stream, EndbBeg) - write(stream, "| Globals:\n") - for i in 0 .. getGlobalLen()-1: - writeLine(stream, getGlobal(i).name) - write(stream, EndbEnd) - -proc debugOut(msg: cstring) = - # the *** *** markers are for easy recognition of debugger - # output for external frontends. - write(stdout, EndbBeg) - write(stdout, "| ") - write(stdout, msg) - write(stdout, EndbEnd) - -proc dbgFatal(msg: cstring) = - debugOut(msg) - dbgAborting = true # the debugger wants to abort - quit(1) - -proc dbgShowCurrentProc(dbgFramePointer: PFrame) = - if dbgFramePointer != nil: - write(stdout, "*** endb| now in proc: ") - write(stdout, dbgFramePointer.procname) - write(stdout, " ***\n") - else: - write(stdout, "*** endb| (proc name not available) ***\n") - -proc dbgShowExecutionPoint() = - write(stdout, "*** endb| ") - write(stdout, framePtr.filename) - write(stdout, "(") - write(stdout, framePtr.line) - write(stdout, ") ") - write(stdout, framePtr.procname) - write(stdout, " ***\n") - -proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int = - result = start - # skip whitespace: - while src[result] in {'\t', ' '}: inc(result) - while true: - case src[result] - of 'a'..'z', '0'..'9': add(a, src[result]) - of '_': discard # just skip it - of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a'))) - else: break - inc(result) - -proc scanWord(src: cstring, a: var StaticStr, start: int): int = - setlen(a) - result = scanAndAppendWord(src, a, start) - -proc scanFilename(src: cstring, a: var StaticStr, start: int): int = - result = start - setLen a - while src[result] in {'\t', ' '}: inc(result) - while src[result] notin {'\t', ' ', '\0'}: - add(a, src[result]) - inc(result) - -proc scanNumber(src: cstring, a: var int, start: int): int = - result = start - a = 0 - while src[result] in {'\t', ' '}: inc(result) - while true: - case src[result] - of '0'..'9': a = a * 10 + ord(src[result]) - ord('0') - of '_': discard # skip underscores (nice for long line numbers) - else: break - inc(result) - -proc dbgHelp() = - debugOut(""" -list of commands (see the manual for further help): - GENERAL -h, help display this help message -q, quit quit the debugger and the program -<ENTER> repeat the previous debugger command - EXECUTING -s, step single step, stepping into routine calls -n, next single step, without stepping into routine calls -f, skipcurrent continue execution until the current routine finishes -c, continue, r, run continue execution until the next breakpoint -i, ignore continue execution, ignore all breakpoints - BREAKPOINTS -b, break [fromline [toline]] [file] - set a new breakpoint for line and file - if line or file are omitted the current one is used -breakpoints display the entire breakpoint list -toggle fromline [file] enable or disable a breakpoint -filenames list all valid filenames - DATA DISPLAY -e, eval <expr> evaluate the expression <expr> -o, out <file> <expr> evaluate <expr> and write it to <file> -w, where display the current execution point -stackframe [file] display current stack frame [and write it to file] -u, up go up in the call stack -d, down go down in the call stack -bt, backtrace display the entire call stack -l, locals display available local variables -g, globals display available global variables -maxdisplay <integer> set the display's recursion maximum -""") - -proc invalidCommand() = - debugOut("[Warning] invalid command ignored (type 'h' for help) ") - -proc hasExt(s: cstring): bool = - # returns true if s has a filename extension - var i = 0 - while s[i] != '\0': - if s[i] == '.': return true - inc i - -proc parseBreakpoint(s: cstring, start: int): Breakpoint = - var dbgTemp: StaticStr - var i = scanNumber(s, result.low, start) - if result.low == 0: result.low = framePtr.line - i = scanNumber(s, result.high, i) - if result.high == 0: result.high = result.low - i = scanFilename(s, dbgTemp, i) - if dbgTemp.len != 0: - if not hasExt(dbgTemp.data): add(dbgTemp, ".nim") - result.filename = canonFilename(dbgTemp.data.cstring) - if result.filename.isNil: - debugOut("[Warning] no breakpoint could be set; unknown filename ") - return - else: - result.filename = framePtr.filename - -proc createBreakPoint(s: cstring, start: int) = - let br = parseBreakpoint(s, start) - if not br.filename.isNil: - if not addBreakpoint(br.filename, br.low, br.high): - debugOut("[Warning] no breakpoint could be set; out of breakpoint space ") - -proc breakpointToggle(s: cstring, start: int) = - var a = parseBreakpoint(s, start) - if not a.filename.isNil: - var b = checkBreakpoints(a.filename, a.low) - if not b.isNil: b.flip - else: debugOut("[Warning] unknown breakpoint ") - -proc dbgEvaluate(stream: File, s: cstring, start: int, f: PFrame) = - var dbgTemp: StaticStr - var i = scanWord(s, dbgTemp, start) - while s[i] in {' ', '\t'}: inc(i) - var v: VarSlot - if s[i] == '.': - inc(i) - add(dbgTemp, '.') - i = scanAndAppendWord(s, dbgTemp, i) - for i in 0 .. getGlobalLen()-1: - let v = getGlobal(i) - if c_strcmp(v.name, dbgTemp.data) == 0: - writeVariable(stream, v) - else: - for i in 0 .. f.len-1: - let v = getLocal(f, i) - if c_strcmp(v.name, dbgTemp.data) == 0: - writeVariable(stream, v) - -proc dbgOut(s: cstring, start: int, currFrame: PFrame) = - var dbgTemp: StaticStr - var i = scanFilename(s, dbgTemp, start) - if dbgTemp.len == 0: - invalidCommand() - return - var stream = openAppend(dbgTemp.data) - if stream == nil: - debugOut("[Warning] could not open or create file ") - return - dbgEvaluate(stream, s, i, currFrame) - close(stream) - -proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) = - var dbgTemp: StaticStr - var i = scanFilename(s, dbgTemp, start) - if dbgTemp.len == 0: - # just write it to stdout: - listFrame(stdout, currFrame) - else: - var stream = openAppend(dbgTemp.data) - if stream == nil: - debugOut("[Warning] could not open or create file ") - return - listFrame(stream, currFrame) - close(stream) - -proc readLine(f: File, line: var StaticStr): bool = - while true: - var c = fgetc(f) - if c < 0'i32: - if line.len > 0: break - else: return false - if c == 10'i32: break # LF - if c == 13'i32: # CR - c = fgetc(f) # is the next char LF? - if c != 10'i32: ungetc(c, f) # no, put the character back - break - add line, chr(int(c)) - result = true - -proc listFilenames() = - write(stdout, EndbBeg) - write(stdout, "| Files:\n") - var i = 0 - while true: - let x = dbgFilenames[i] - if x.isNil: break - write(stdout, x) - write(stdout, "\n") - inc i - write(stdout, EndbEnd) - -proc dbgWriteStackTrace(f: PFrame) -proc commandPrompt() = - # if we return from this routine, user code executes again - var - again = true - dbgFramePtr = framePtr # for going down and up the stack - dbgDown = 0 # how often we did go down - dbgTemp: StaticStr - - while again: - write(stdout, "*** endb| >>") - let oldLen = dbgUser.len - dbgUser.len = 0 - if not readLine(stdin, dbgUser): break - if dbgUser.len == 0: dbgUser.len = oldLen - # now look what we have to do: - var i = scanWord(dbgUser.data, dbgTemp, 0) - template `?`(x: expr): expr = dbgTemp == cstring(x) - if ?"s" or ?"step": - dbgState = dbStepInto - again = false - elif ?"n" or ?"next": - dbgState = dbStepOver - dbgSkipToFrame = framePtr - again = false - elif ?"f" or ?"skipcurrent": - dbgState = dbSkipCurrent - dbgSkipToFrame = framePtr.prev - again = false - elif ?"c" or ?"continue" or ?"r" or ?"run": - dbgState = dbBreakpoints - again = false - elif ?"i" or ?"ignore": - dbgState = dbOff - again = false - elif ?"h" or ?"help": - dbgHelp() - elif ?"q" or ?"quit": - dbgState = dbQuiting - dbgAborting = true - again = false - quit(1) # BUGFIX: quit with error code > 0 - elif ?"e" or ?"eval": - var - prevState = dbgState - prevSkipFrame = dbgSkipToFrame - dbgState = dbSkipCurrent - dbgEvaluate(stdout, dbgUser.data, i, dbgFramePtr) - dbgState = prevState - dbgSkipToFrame = prevSkipFrame - elif ?"o" or ?"out": - dbgOut(dbgUser.data, i, dbgFramePtr) - elif ?"stackframe": - dbgStackFrame(dbgUser.data, i, dbgFramePtr) - elif ?"w" or ?"where": - dbgShowExecutionPoint() - elif ?"l" or ?"locals": - var - prevState = dbgState - prevSkipFrame = dbgSkipToFrame - dbgState = dbSkipCurrent - listLocals(stdout, dbgFramePtr) - dbgState = prevState - dbgSkipToFrame = prevSkipFrame - elif ?"g" or ?"globals": - var - prevState = dbgState - prevSkipFrame = dbgSkipToFrame - dbgState = dbSkipCurrent - listGlobals(stdout) - dbgState = prevState - dbgSkipToFrame = prevSkipFrame - elif ?"u" or ?"up": - if dbgDown <= 0: - debugOut("[Warning] cannot go up any further ") - else: - dbgFramePtr = framePtr - for j in 0 .. dbgDown-2: # BUGFIX - dbgFramePtr = dbgFramePtr.prev - dec(dbgDown) - dbgShowCurrentProc(dbgFramePtr) - elif ?"d" or ?"down": - if dbgFramePtr != nil: - inc(dbgDown) - dbgFramePtr = dbgFramePtr.prev - dbgShowCurrentProc(dbgFramePtr) - else: - debugOut("[Warning] cannot go down any further ") - elif ?"bt" or ?"backtrace": - dbgWriteStackTrace(framePtr) - elif ?"b" or ?"break": - createBreakPoint(dbgUser.data, i) - elif ?"breakpoints": - listBreakPoints() - elif ?"toggle": - breakpointToggle(dbgUser.data, i) - elif ?"filenames": - listFilenames() - elif ?"maxdisplay": - var parsed: int - i = scanNumber(dbgUser.data, parsed, i) - if dbgUser.data[i-1] in {'0'..'9'}: - if parsed == 0: maxDisplayRecDepth = -1 - else: maxDisplayRecDepth = parsed - else: - invalidCommand() - else: invalidCommand() - -proc endbStep() = - # we get into here if an unhandled exception has been raised - # XXX: do not allow the user to run the program any further? - # XXX: BUG: the frame is lost here! - dbgShowExecutionPoint() - commandPrompt() - -proc dbgWriteStackTrace(f: PFrame) = - const - firstCalls = 32 - var - it = f - i = 0 - total = 0 - tempFrames: array [0..127, PFrame] - # setup long head: - while it != nil and i <= high(tempFrames)-firstCalls: - tempFrames[i] = it - inc(i) - inc(total) - it = it.prev - # go up the stack to count 'total': - var b = it - while it != nil: - inc(total) - it = it.prev - var skipped = 0 - if total > len(tempFrames): - # skip N - skipped = total-i-firstCalls+1 - for j in 1..skipped: - if b != nil: b = b.prev - # create '...' entry: - tempFrames[i] = nil - inc(i) - # setup short tail: - while b != nil and i <= high(tempFrames): - tempFrames[i] = b - inc(i) - b = b.prev - for j in countdown(i-1, 0): - if tempFrames[j] == nil: - write(stdout, "(") - write(stdout, skipped) - write(stdout, " calls omitted) ...") - else: - write(stdout, tempFrames[j].filename) - if tempFrames[j].line > 0: - write(stdout, '(') - write(stdout, tempFrames[j].line) - write(stdout, ')') - write(stdout, ' ') - write(stdout, tempFrames[j].procname) - write(stdout, "\n") - -proc checkForBreakpoint = - let b = checkBreakpoints(framePtr.filename, framePtr.line) - if b != nil: - write(stdout, "*** endb| reached ") - write(stdout, framePtr.filename) - write(stdout, "(") - write(stdout, framePtr.line) - write(stdout, ") ") - write(stdout, framePtr.procname) - write(stdout, " ***\n") - commandPrompt() - -proc lineHookImpl() {.nimcall.} = - case dbgState - of dbStepInto: - # we really want the command prompt here: - dbgShowExecutionPoint() - commandPrompt() - of dbSkipCurrent, dbStepOver: # skip current routine - if framePtr == dbgSkipToFrame: - dbgShowExecutionPoint() - commandPrompt() - else: - # breakpoints are wanted though (I guess) - checkForBreakpoint() - of dbBreakpoints: - # debugger is only interested in breakpoints - checkForBreakpoint() - else: discard - -proc watchpointHookImpl(name: cstring) {.nimcall.} = - dbgWriteStackTrace(framePtr) - debugOut(name) - -proc initDebugger {.inline.} = - dbgState = dbStepInto - dbgUser.len = 1 - dbgUser.data[0] = 's' - dbgWatchpointHook = watchpointHookImpl - dbgLineHook = lineHookImpl |