Files
Nim/lib/system/stacktraces.nim
Andreas Rumpf 1fae66e4df 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>
2020-09-22 13:03:24 +02:00

84 lines
3.8 KiB
Nim

#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Additional code for customizable stack traces. Unstable API, for internal
# usage only.
const
reraisedFromBegin* = -10
reraisedFromEnd* = -100
maxStackTraceLines* = 128
when defined(nimStackTraceOverride):
## Procedure types for overriding the default stack trace.
type
cuintptr_t {.importc: "uintptr_t", nodecl.} = uint
## This is the same as the type ``uintptr_t`` in C.
StackTraceOverrideGetTracebackProc* = proc (): string {.
nimcall, gcsafe, locks: 0, raises: [], tags: [].}
StackTraceOverrideGetProgramCountersProc* = proc (maxLength: cint): seq[cuintptr_t] {.
nimcall, gcsafe, locks: 0, raises: [], tags: [].}
StackTraceOverrideGetDebuggingInfoProc* =
proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.
nimcall, gcsafe, locks: 0, raises: [], tags: [].}
# Default procedures (not normally used, because people opting in on this
# override are supposed to register their own versions).
var
stackTraceOverrideGetTraceback: StackTraceOverrideGetTracebackProc =
proc (): string {.nimcall, gcsafe, locks: 0, raises: [], tags: [].} =
discard
#result = "Stack trace override procedure not registered.\n"
stackTraceOverrideGetProgramCounters: StackTraceOverrideGetProgramCountersProc =
proc (maxLength: cint): seq[cuintptr_t] {.nimcall, gcsafe, locks: 0, raises: [], tags: [].} =
discard
stackTraceOverrideGetDebuggingInfo: StackTraceOverrideGetDebuggingInfoProc =
proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.
nimcall, gcsafe, locks: 0, raises: [], tags: [].} =
discard
# Custom procedure registration.
proc registerStackTraceOverride*(overrideProc: StackTraceOverrideGetTracebackProc) =
## Override the default stack trace inside rawWriteStackTrace() with your
## own procedure.
stackTraceOverrideGetTraceback = overrideProc
proc registerStackTraceOverrideGetProgramCounters*(overrideProc: StackTraceOverrideGetProgramCountersProc) =
stackTraceOverrideGetProgramCounters = overrideProc
proc registerStackTraceOverrideGetDebuggingInfo*(overrideProc: StackTraceOverrideGetDebuggingInfoProc) =
stackTraceOverrideGetDebuggingInfo = overrideProc
# Custom stack trace manipulation.
proc auxWriteStackTraceWithOverride*(s: var string) =
add(s, stackTraceOverrideGetTraceback())
proc auxWriteStackTraceWithOverride*(s: var seq[StackTraceEntry]) =
let programCounters = stackTraceOverrideGetProgramCounters(maxStackTraceLines)
if s.len == 0:
s = newSeqOfCap[StackTraceEntry](programCounters.len)
for programCounter in programCounters:
s.add(StackTraceEntry(programCounter: cast[uint](programCounter)))
# We may have more stack trace lines in the output, due to inlined procedures.
proc addDebuggingInfo*(s: seq[StackTraceEntry]): seq[StackTraceEntry] =
var programCounters: seq[cuintptr_t]
# We process program counters in groups from complete stack traces, because
# we have logic that keeps track of certain functions being inlined or not.
for entry in s:
if entry.procname.isNil and entry.programCounter != 0:
programCounters.add(cast[cuintptr_t](entry.programCounter))
elif entry.procname.isNil and (entry.line == reraisedFromBegin or entry.line == reraisedFromEnd):
result.add(stackTraceOverrideGetDebuggingInfo(programCounters, maxStackTraceLines))
programCounters = @[]
result.add(entry)
else:
result.add(entry)
if programCounters.len > 0:
result.add(stackTraceOverrideGetDebuggingInfo(programCounters, maxStackTraceLines))