diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-11-16 22:57:18 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-11-16 22:57:27 +0100 |
commit | 661ce8b8cbf6d1a2f17934d0998bb630e3a95900 (patch) | |
tree | 25fa58fcef7d1267044ed5ec20b9b30639ed8004 | |
parent | 42be656b9481862667586f1ac4883e6d6863079e (diff) | |
download | Nim-661ce8b8cbf6d1a2f17934d0998bb630e3a95900.tar.gz |
added system.getStackTraceEntries
-rw-r--r-- | changelog.md | 2 | ||||
-rw-r--r-- | lib/system.nim | 12 | ||||
-rw-r--r-- | lib/system/excpt.nim | 85 |
3 files changed, 77 insertions, 22 deletions
diff --git a/changelog.md b/changelog.md index 3fdfa47d8..1d4d43f33 100644 --- a/changelog.md +++ b/changelog.md @@ -87,3 +87,5 @@ This now needs to be written as: - The ``nim doc`` command is now an alias for ``nim doc2``, the second version of the documentation generator. The old version 1 can still be accessed via the new ``nim doc0`` command. +- Added ``system.getStackTraceEntries`` that allows you to access the stack + trace in a structured manner without string parsing. diff --git a/lib/system.nim b/lib/system.nim index 51ffeb042..4b0eb21b6 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -456,6 +456,13 @@ type WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation. ExecIOEffect* = object of IOEffect ## Effect describing an executing IO operation. + StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led + ## to them. A StackTraceEntry is a single entry of the + ## stack trace. + procname*: cstring ## name of the proc that is currently executing + line*: int ## line number of the proc that is currently executing + filename*: cstring ## filename of the proc that is currently executing + Exception* {.compilerproc.} = object of RootObj ## \ ## Base exception class. ## @@ -468,7 +475,10 @@ type msg* {.exportc: "message".}: string ## the exception's message. Not ## providing an exception message ## is bad style. - trace: string + when defined(js): + trace: string + else: + trace: seq[StackTraceEntry] up: ref Exception # used for stacking exceptions. Not exported! SystemError* = object of Exception ## \ diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 9e02d6824..70c18ae21 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -155,6 +155,52 @@ when not hasThreadSupport: var tempFrames: array[0..127, PFrame] # should not be alloc'd on stack +const + reraisedFromBegin = -10 + reraisedFromEnd = -100 + +template reraisedFrom(z): untyped = + StackTraceEntry(procname: nil, line: z, filename: nil) + +proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) = + var + it = f + i = 0 + while it != nil: + inc(i) + it = it.prev + var last = i-1 + if s.isNil: + s = newSeq[StackTraceEntry](i) + else: + last = s.len + i - 1 + s.setLen(last+1) + it = f + while it != nil: + s[last] = StackTraceEntry(procname: it.procname, + line: it.line, + filename: it.filename) + it = it.prev + dec last + +template addFrameEntry(s, f: untyped) = + var oldLen = s.len + add(s, f.filename) + if f.line > 0: + add(s, '(') + add(s, $f.line) + add(s, ')') + for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') + add(s, f.procname) + add(s, "\n") + +proc `$`(s: seq[StackTraceEntry]): string = + result = newStringOfCap(2000) + for i in 0 .. s.len-1: + if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n" + elif s[i].line == reraisedFromEnd: result.add "]]\n" + else: addFrameEntry(result, s[i]) + proc auxWriteStackTrace(f: PFrame, s: var string) = when hasThreadSupport: var @@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = if tempFrames[j] == nil: add(s, "(") add(s, $skipped) - add(s, " calls omitted) ...") + add(s, " calls omitted) ...\n") else: - var oldLen = s.len - add(s, tempFrames[j].filename) - if tempFrames[j].line > 0: - add(s, '(') - add(s, $tempFrames[j].line) - add(s, ')') - for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') - add(s, tempFrames[j].procname) - add(s, "\n") + addFrameEntry(s, tempFrames[j]) proc stackTraceAvailable*(): bool @@ -221,6 +259,13 @@ when hasSomeStackTrace: auxWriteStackTraceWithBacktrace(s) else: add(s, "No stack traceback available\n") + + proc rawWriteStackTrace(s: var seq[StackTraceEntry]) = + when NimStackTrace: + auxWriteStackTrace(framePtr, s) + else: + s = nil + proc stackTraceAvailable(): bool = when NimStackTrace: if framePtr == nil: @@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} = else: endbStep() # call the debugger -when false: - proc rawRaise*(e: ref Exception) = - ## undocumented. Do not use. - pushCurrentException(e) - c_longjmp(excHandler.context, 1) - var onUnhandledException*: (proc (errorMsg: string) {. nimcall.}) ## set this error \ ## handler to override the existing behaviour on an unhandled exception. @@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) = when hasSomeStackTrace: var buf = newStringOfCap(2000) if isNil(e.trace): rawWriteStackTrace(buf) - else: add(buf, e.trace) + else: add(buf, $e.trace) add(buf, "Error: unhandled exception: ") if not isNil(e.msg): add(buf, e.msg) add(buf, " [") @@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = if e.name.isNil: e.name = ename when hasSomeStackTrace: if e.trace.isNil: - e.trace = "" rawWriteStackTrace(e.trace) elif framePtr != nil: - e.trace.add "[[reraised from:\n" + e.trace.add reraisedFrom(reraisedFromBegin) auxWriteStackTrace(framePtr, e.trace) - e.trace.add "]]\n" + e.trace.add reraisedFrom(reraisedFromEnd) raiseExceptionAux(e) proc reraiseException() {.compilerRtl.} = @@ -349,10 +387,15 @@ proc getStackTrace(): string = proc getStackTrace(e: ref Exception): string = if not isNil(e) and not isNil(e.trace): - result = e.trace + result = $e.trace else: result = "" +proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = + ## Returns the attached stack trace to the exception ``e`` as + ## a ``seq``. This is not yet available for the JS backend. + shallowCopy(result, e.trace) + when defined(nimRequiresNimFrame): proc stackOverflow() {.noinline.} = writeStackTrace() |