summary refs log tree commit diff stats
path: root/lib/system/stacktraces.nim
blob: dce25fdc08fbcf18da4ed089740bc00108b1e5d5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#
#
#            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))