added system.getStackTraceEntries

This commit is contained in:
Andreas Rumpf 2017-11-16 22:57:18 +01:00
commit 661ce8b8cb
3 changed files with 77 additions and 22 deletions

View file

@ -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 ``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 the documentation generator. The old version 1 can still be accessed
via the new ``nim doc0`` command. via the new ``nim doc0`` command.
- Added ``system.getStackTraceEntries`` that allows you to access the stack
trace in a structured manner without string parsing.

View file

@ -456,6 +456,13 @@ type
WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation. WriteIOEffect* = object of IOEffect ## Effect describing a write IO operation.
ExecIOEffect* = object of IOEffect ## Effect describing an executing 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 ## \ Exception* {.compilerproc.} = object of RootObj ## \
## Base exception class. ## Base exception class.
## ##
@ -468,7 +475,10 @@ type
msg* {.exportc: "message".}: string ## the exception's message. Not msg* {.exportc: "message".}: string ## the exception's message. Not
## providing an exception message ## providing an exception message
## is bad style. ## is bad style.
when defined(js):
trace: string trace: string
else:
trace: seq[StackTraceEntry]
up: ref Exception # used for stacking exceptions. Not exported! up: ref Exception # used for stacking exceptions. Not exported!
SystemError* = object of Exception ## \ SystemError* = object of Exception ## \

View file

@ -155,6 +155,52 @@ when not hasThreadSupport:
var var
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack 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) = proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport: when hasThreadSupport:
var var
@ -194,17 +240,9 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
if tempFrames[j] == nil: if tempFrames[j] == nil:
add(s, "(") add(s, "(")
add(s, $skipped) add(s, $skipped)
add(s, " calls omitted) ...") add(s, " calls omitted) ...\n")
else: else:
var oldLen = s.len addFrameEntry(s, tempFrames[j])
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")
proc stackTraceAvailable*(): bool proc stackTraceAvailable*(): bool
@ -221,6 +259,13 @@ when hasSomeStackTrace:
auxWriteStackTraceWithBacktrace(s) auxWriteStackTraceWithBacktrace(s)
else: else:
add(s, "No stack traceback available\n") add(s, "No stack traceback available\n")
proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
when NimStackTrace:
auxWriteStackTrace(framePtr, s)
else:
s = nil
proc stackTraceAvailable(): bool = proc stackTraceAvailable(): bool =
when NimStackTrace: when NimStackTrace:
if framePtr == nil: if framePtr == nil:
@ -240,12 +285,6 @@ proc quitOrDebug() {.inline.} =
else: else:
endbStep() # call the debugger 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) {. var onUnhandledException*: (proc (errorMsg: string) {.
nimcall.}) ## set this error \ nimcall.}) ## set this error \
## handler to override the existing behaviour on an unhandled exception. ## handler to override the existing behaviour on an unhandled exception.
@ -282,7 +321,7 @@ proc raiseExceptionAux(e: ref Exception) =
when hasSomeStackTrace: when hasSomeStackTrace:
var buf = newStringOfCap(2000) var buf = newStringOfCap(2000)
if isNil(e.trace): rawWriteStackTrace(buf) if isNil(e.trace): rawWriteStackTrace(buf)
else: add(buf, e.trace) else: add(buf, $e.trace)
add(buf, "Error: unhandled exception: ") add(buf, "Error: unhandled exception: ")
if not isNil(e.msg): add(buf, e.msg) if not isNil(e.msg): add(buf, e.msg)
add(buf, " [") add(buf, " [")
@ -318,12 +357,11 @@ proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename if e.name.isNil: e.name = ename
when hasSomeStackTrace: when hasSomeStackTrace:
if e.trace.isNil: if e.trace.isNil:
e.trace = ""
rawWriteStackTrace(e.trace) rawWriteStackTrace(e.trace)
elif framePtr != nil: elif framePtr != nil:
e.trace.add "[[reraised from:\n" e.trace.add reraisedFrom(reraisedFromBegin)
auxWriteStackTrace(framePtr, e.trace) auxWriteStackTrace(framePtr, e.trace)
e.trace.add "]]\n" e.trace.add reraisedFrom(reraisedFromEnd)
raiseExceptionAux(e) raiseExceptionAux(e)
proc reraiseException() {.compilerRtl.} = proc reraiseException() {.compilerRtl.} =
@ -349,10 +387,15 @@ proc getStackTrace(): string =
proc getStackTrace(e: ref Exception): string = proc getStackTrace(e: ref Exception): string =
if not isNil(e) and not isNil(e.trace): if not isNil(e) and not isNil(e.trace):
result = e.trace result = $e.trace
else: else:
result = "" 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): when defined(nimRequiresNimFrame):
proc stackOverflow() {.noinline.} = proc stackOverflow() {.noinline.} =
writeStackTrace() writeStackTrace()