better nativestacktrace support; refs #15284; backport [1.2] (#15384)

* nimStackTraceOverride: enable stack traces in exceptions

This is a two-step stack trace collection scheme, because re-raised
exceptions will collect multiple stack traces but use them rarely, when
printing info about an uncaught exception, so it makes sense to only do
the cheap stack unwinding all the time and the relatively expensive
debugging information collection on-demand.

`asyncfutures` implements its own `$` proc for printing
`seq[StackTraceEntry]`, so we have to add the debugging info there, just
like we do for the private `$` proc in `system/excpt`.

* cleaned up PR #15284

Co-authored-by: Ștefan Talpalaru <stefantalpalaru@yahoo.com>
This commit is contained in:
Andreas Rumpf 2020-09-22 13:03:24 +02:00 • committed by GitHub
commit 1fae66e4df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 142 additions and 28 deletions

View file

@ -11,6 +11,7 @@
# use the heap (and nor exceptions) do not include the GC or memory allocator.
import std/private/miscdollars
import stacktraces
var
errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign,
@ -137,20 +138,6 @@ const
hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or
(defined(nativeStackTrace) and nativeStackTraceSupported)
when defined(nimStackTraceOverride):
type StackTraceOverrideProc* = proc (): string {.nimcall, noinline, benign, raises: [], tags: [].}
## Procedure type for overriding the default stack trace.
var stackTraceOverrideGetTraceback: StackTraceOverrideProc = proc(): string {.noinline.} =
result = "Stack trace override procedure not registered.\n"
proc registerStackTraceOverride*(overrideProc: StackTraceOverrideProc) =
## Override the default stack trace inside rawWriteStackTrace() with your
## own procedure.
stackTraceOverrideGetTraceback = overrideProc
proc auxWriteStackTraceWithOverride(s: var string) =
add(s, stackTraceOverrideGetTraceback())
when defined(nativeStacktrace) and nativeStackTraceSupported:
type
@ -168,13 +155,13 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
when not hasThreadSupport:
var
tempAddresses: array[0..127, pointer] # should not be alloc'd on stack
tempAddresses: array[maxStackTraceLines, pointer] # should not be alloc'd on stack
tempDlInfo: TDl_info
proc auxWriteStackTraceWithBacktrace(s: var string) =
when hasThreadSupport:
var
tempAddresses: array[0..127, pointer] # but better than a threadvar
tempAddresses: array[maxStackTraceLines, pointer] # but better than a threadvar
tempDlInfo: TDl_info
# This is allowed to be expensive since it only happens during crashes
# (but this way you don't need manual stack tracing)
@ -202,11 +189,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
when hasSomeStackTrace and not hasThreadSupport:
var
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
const
reraisedFromBegin = -10
reraisedFromEnd = -100
tempFrames: array[maxStackTraceLines, PFrame] # should not be alloc'd on stack
template reraisedFrom(z): untyped =
StackTraceEntry(procname: nil, line: z, filename: nil)
@ -253,7 +236,12 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
for i in first..<f.frameMsgLen: add(s, frameMsgBuf[i])
add(s, "\n")
proc `$`(s: seq[StackTraceEntry]): string =
proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
when defined(nimStackTraceOverride):
let s = addDebuggingInfo(stackTraceEntries)
else:
let s = stackTraceEntries
result = newStringOfCap(2000)
for i in 0 .. s.len-1:
if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
@ -265,7 +253,7 @@ when hasSomeStackTrace:
proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport:
var
tempFrames: array[0..127, PFrame] # but better than a threadvar
tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
const
firstCalls = 32
var
@ -324,7 +312,9 @@ when hasSomeStackTrace:
add(s, "No stack traceback available\n")
proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
when NimStackTrace:
when defined(nimStackTraceOverride):
auxWriteStackTraceWithOverride(s)
elif NimStackTrace:
auxWriteStackTrace(framePtr, s)
else:
s = @[]
@ -468,7 +458,12 @@ proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring
if e.name.isNil: e.name = ename
when hasSomeStackTrace:
when defined(nimStackTraceOverride):
e.trace = @[]
if e.trace.len == 0:
rawWriteStackTrace(e.trace)
else:
e.trace.add reraisedFrom(reraisedFromBegin)
auxWriteStackTraceWithOverride(e.trace)
e.trace.add reraisedFrom(reraisedFromEnd)
elif NimStackTrace:
if e.trace.len == 0:
rawWriteStackTrace(e.trace)