added system.getStackTraceEntries
This commit is contained in:
parent
42be656b94
commit
661ce8b8cb
3 changed files with 77 additions and 22 deletions
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
trace: string
|
when defined(js):
|
||||||
|
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 ## \
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue