diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/nimbase.h | 1 | ||||
-rw-r--r-- | lib/std/stackframes.nim | 30 | ||||
-rw-r--r-- | lib/system.nim | 36 | ||||
-rw-r--r-- | lib/system/exceptions.nim | 9 | ||||
-rw-r--r-- | lib/system/excpt.nim | 24 |
5 files changed, 82 insertions, 18 deletions
diff --git a/lib/nimbase.h b/lib/nimbase.h index eb750864a..5b0dcc2c8 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -489,6 +489,7 @@ struct TFrame_ { NCSTRING filename; NI16 len; NI16 calldepth; + NI frameMsgLen; }; #define NIM_POSIX_INIT __attribute__((constructor)) diff --git a/lib/std/stackframes.nim b/lib/std/stackframes.nim new file mode 100644 index 000000000..dbd866536 --- /dev/null +++ b/lib/std/stackframes.nim @@ -0,0 +1,30 @@ +const NimStackTrace = compileOption("stacktrace") +const NimStackTraceMsgs = compileOption("stacktraceMsgs") + +template procName*(): string = + ## returns current C/C++ function name + when defined(c) or defined(cpp): + var name {.inject.}: cstring + {.emit: "`name` = __func__;".} + $name + +template getPFrame*(): PFrame = + ## avoids a function call (unlike `getFrame()`) + block: + when NimStackTrace: + var framePtr {.inject.}: PFrame + {.emit: "`framePtr` = &FR_;".} + framePtr + +template setFrameMsg*(msg: string, prefix = " ") = + ## attach a msg to current `PFrame`. This can be called multiple times + ## in a given PFrame. Noop unless passing --stacktraceMsgs and --stacktrace + when NimStackTrace and NimStackTraceMsgs: + block: + var fr {.inject.}: PFrame + {.emit: "`fr` = &FR_;".} + # consider setting a custom upper limit on size (analog to stack overflow) + frameMsgBuf.setLen fr.frameMsgLen + frameMsgBuf.add prefix + frameMsgBuf.add msg + fr.frameMsgLen += prefix.len + msg.len diff --git a/lib/system.nim b/lib/system.nim index cd941b19f..469821fab 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -54,6 +54,23 @@ type include "system/basic_types" + +proc compileOption*(option: string): bool {. + magic: "CompileOption", noSideEffect.} + ## Can be used to determine an `on|off` compile-time option. Example: + ## + ## .. code-block:: Nim + ## when compileOption("floatchecks"): + ## echo "compiled with floating point NaN and Inf checks" + +proc compileOption*(option, arg: string): bool {. + magic: "CompileOptionArg", noSideEffect.} + ## Can be used to determine an enum compile-time option. Example: + ## + ## .. code-block:: Nim + ## when compileOption("opt", "size") and compileOption("gc", "boehm"): + ## echo "compiled with optimization for size and uses Boehm's GC" + {.push warning[GcMem]: off, warning[Uninit]: off.} {.push hints: off.} @@ -1040,22 +1057,6 @@ const # emit this flag # for string literals, it allows for some optimizations. -proc compileOption*(option: string): bool {. - magic: "CompileOption", noSideEffect.} - ## Can be used to determine an `on|off` compile-time option. Example: - ## - ## .. code-block:: Nim - ## when compileOption("floatchecks"): - ## echo "compiled with floating point NaN and Inf checks" - -proc compileOption*(option, arg: string): bool {. - magic: "CompileOptionArg", noSideEffect.} - ## Can be used to determine an enum compile-time option. Example: - ## - ## .. code-block:: Nim - ## when compileOption("opt", "size") and compileOption("gc", "boehm"): - ## echo "compiled with optimization for size and uses Boehm's GC" - const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own @@ -1891,6 +1892,7 @@ var type PFrame* = ptr TFrame ## Represents a runtime frame of the call stack; ## part of the debugger API. + # keep in sync with nimbase.h `struct TFrame_` TFrame* {.importc, nodecl, final.} = object ## The frame itself. prev*: PFrame ## Previous frame; used for chaining the call stack. procname*: cstring ## Name of the proc that is currently executing. @@ -1898,6 +1900,8 @@ type filename*: cstring ## Filename of the proc that is currently executing. len*: int16 ## Length of the inspectable slots. calldepth*: int16 ## Used for max call depth checking. + when NimStackTraceMsgs: + frameMsgLen*: int ## end position in frameMsgBuf for this frame. when defined(js): proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim index 3979fb66e..516de8252 100644 --- a/lib/system/exceptions.nim +++ b/lib/system/exceptions.nim @@ -1,3 +1,7 @@ +const NimStackTraceMsgs = + when defined(nimHasStacktraceMsgs): compileOption("stacktraceMsgs") + else: false + type RootEffect* {.compilerproc.} = object of RootObj ## \ ## Base effect class. @@ -16,6 +20,11 @@ type 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. + when NimStackTraceMsgs: + frameMsg*: string ## When a stacktrace is generated in a given frame and + ## rendered at a later time, we should ensure the stacktrace + ## data isn't invalidated; any pointer into PFrame is + ## subject to being invalidated so shouldn't be stored. Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \ ## Base exception class. diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 8ac47d26b..76d188ea6 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -53,6 +53,8 @@ type len: int prev: ptr GcFrameHeader +when NimStackTraceMsgs: + var frameMsgBuf* {.threadvar.}: string var framePtr {.threadvar.}: PFrame excHandler {.threadvar.}: PSafePoint @@ -224,10 +226,17 @@ proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) = s[last] = StackTraceEntry(procname: it.procname, line: it.line, filename: it.filename) + when NimStackTraceMsgs: + let first = if it.prev == nil: 0 else: it.prev.frameMsgLen + if it.frameMsgLen > first: + s[last].frameMsg.setLen(it.frameMsgLen - first) + # somehow string slicing not available here + for i in first .. it.frameMsgLen-1: + s[last].frameMsg[i-first] = frameMsgBuf[i] it = it.prev dec last -template addFrameEntry(s, f: untyped) = +template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) = var oldLen = s.len add(s, f.filename) if f.line > 0: @@ -236,6 +245,12 @@ template addFrameEntry(s, f: untyped) = add(s, ')') for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') add(s, f.procname) + when NimStackTraceMsgs: + when type(f) is StackTraceEntry: + add(s, f.frameMsg) + else: + var first = if f.prev == nil: 0 else: f.prev.frameMsgLen + for i in first..<f.frameMsgLen: add(s, frameMsgBuf[i]) add(s, "\n") proc `$`(s: seq[StackTraceEntry]): string = @@ -519,7 +534,12 @@ proc callDepthLimitReached() {.noinline.} = quit(1) proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = - s.calldepth = if framePtr == nil: 0 else: framePtr.calldepth+1 + if framePtr == nil: + s.calldepth = 0 + when NimStackTraceMsgs: s.frameMsgLen = 0 + else: + s.calldepth = framePtr.calldepth+1 + when NimStackTraceMsgs: s.frameMsgLen = framePtr.frameMsgLen s.prev = framePtr framePtr = s if s.calldepth == nimCallDepthLimit: callDepthLimitReached() |