nim-libbacktrace/libbacktrace.nim

120 lines
4.1 KiB
Nim

# Copyright (c) 2019-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0,
# * MIT license
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
when not compileOption("debuginfo"):
stderr.write("libbacktrace error: no debugging symbols available. Compile with '--debugger:native'.\n")
stderr.flushFile()
when defined(nimStackTraceOverride) and defined(nimHasStacktracesModule):
import system/stacktraces
# Don't warn that this module is unused (e.g.: when the Nim compiler supports it
# and users need to import it, even if they don't call getBacktrace() manually).
{.used.}
# There is no "copyMem()" in Nimscript, so "getBacktrace()" will not work in
# there, but we might still want to import this module with a global
# "--import:libbacktrace" Nim compiler flag.
when not (defined(nimscript) or defined(js)):
import std/algorithm, libbacktrace/wrapper, std/os, system/ansi_c, std/strutils
const
topLevelPath = currentSourcePath.parentDir().replace('\\', '/')
installPath = topLevelPath & "/install/usr"
{.passc: "-I" & escape(topLevelPath).}
when defined(cpp):
{.passl: escape(installPath & "/lib/libbacktracenimcpp.a").}
else:
{.passl: escape(installPath & "/lib/libbacktracenim.a").}
when defined(libbacktraceUseSystemLibs):
{.passl: "-lbacktrace".}
when defined(macosx) or defined(windows):
{.passl: "-lunwind".}
else:
{.passc: "-I" & escape(installPath & "/include").}
{.passl: escape(installPath & "/lib/libbacktrace.a").}
when defined(macosx) or defined(windows):
{.passl: escape(installPath & "/lib/libunwind.a").}
when defined(windows):
{.passl: "-lpsapi".}
proc getBacktrace*(): string {.noinline.} =
let
# bt: cstring = get_backtrace_c()
bt: cstring = get_backtrace_max_length_c(max_length = 128, skip = 3)
btLen = len(bt)
result = newString(btLen)
if btLen > 0:
copyMem(addr(result[0]), bt, btLen)
c_free(bt)
when defined(nimStackTraceOverride) and declared(registerStackTraceOverride):
registerStackTraceOverride(libbacktrace.getBacktrace)
proc getProgramCounters*(maxLength: cint): seq[cuintptr_t] {.noinline.} =
result = newSeqOfCap[cuintptr_t](maxLength)
var
pcPtr = get_program_counters_c(max_length = maxLength, skip = 2)
iPtr = pcPtr
while iPtr[] != 0:
result.add(iPtr[])
iPtr = cast[ptr cuintptr_t](cast[uint](iPtr) + sizeof(cuintptr_t).uint)
c_free(pcPtr)
when defined(nimStackTraceOverride) and declared(registerStackTraceOverrideGetProgramCounters):
registerStackTraceOverrideGetProgramCounters(libbacktrace.getProgramCounters)
proc getDebuggingInfo*(programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.noinline.} =
result = newSeqOfCap[StackTraceEntry](maxLength)
if programCounters.len == 0:
return
var
functionInfoPtr = get_debugging_info_c(unsafeAddr programCounters[0], maxLength)
iPtr = functionInfoPtr
res: StackTraceEntry
while iPtr[].filename != nil:
# Older stdlib doesn't have this field in "StackTraceEntry".
when compiles(res.filenameStr):
let filenameLen = len(iPtr[].filename)
res.filenameStr = newString(filenameLen)
if filenameLen > 0:
copyMem(addr(res.filenameStr[0]), iPtr[].filename, filenameLen)
res.filename = res.filenameStr
res.line = iPtr[].lineno
when compiles(res.procnameStr):
let functionLen = len(iPtr[].function)
res.procnameStr = newString(functionLen)
if functionLen > 0:
copyMem(addr(res.procnameStr[0]), iPtr[].function, functionLen)
res.procname = res.procnameStr
c_free(iPtr[].filename)
c_free(iPtr[].function)
iPtr = cast[ptr DebuggingInfo](cast[uint](iPtr) + sizeof(DebuggingInfo).uint)
result.add(res)
c_free(functionInfoPtr)
# Nim convention.
reverse(result)
when defined(nimStackTraceOverride) and declared(registerStackTraceOverrideGetDebuggingInfo):
registerStackTraceOverrideGetDebuggingInfo(libbacktrace.getDebuggingInfo)