2024-03-26 18:00:23 +00:00
|
|
|
# Copyright (c) 2019-2024 Status Research & Development GmbH
|
2019-12-15 23:21:43 +00:00
|
|
|
# Licensed under either of
|
2019-12-19 20:14:59 +00:00
|
|
|
# * Apache License, version 2.0,
|
|
|
|
# * MIT license
|
2019-12-15 23:21:43 +00:00
|
|
|
# at your option.
|
|
|
|
# This file may not be copied, modified, or distributed except according to
|
|
|
|
# those terms.
|
|
|
|
|
2021-04-26 17:19:47 +00:00
|
|
|
when not compileOption("debuginfo"):
|
|
|
|
stderr.write("libbacktrace error: no debugging symbols available. Compile with '--debugger:native'.\n")
|
|
|
|
stderr.flushFile()
|
|
|
|
|
2020-09-23 10:19:32 +00:00
|
|
|
when defined(nimStackTraceOverride) and defined(nimHasStacktracesModule):
|
|
|
|
import system/stacktraces
|
|
|
|
|
2019-12-15 23:21:43 +00:00
|
|
|
# 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.}
|
|
|
|
|
2020-06-16 19:09:07 +00:00
|
|
|
# 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)):
|
2024-03-26 19:09:04 +00:00
|
|
|
import std/algorithm, libbacktrace/wrapper, std/os, system/ansi_c, std/strutils
|
2019-12-15 23:21:43 +00:00
|
|
|
|
2022-03-22 15:04:03 +00:00
|
|
|
const
|
|
|
|
topLevelPath = currentSourcePath.parentDir().replace('\\', '/')
|
|
|
|
installPath = topLevelPath & "/install/usr"
|
2019-12-19 20:14:59 +00:00
|
|
|
|
2024-03-26 19:09:04 +00:00
|
|
|
{.passc: "-I" & escape(topLevelPath).}
|
2019-12-15 23:21:43 +00:00
|
|
|
|
2020-06-16 19:09:07 +00:00
|
|
|
when defined(cpp):
|
2024-03-26 19:09:04 +00:00
|
|
|
{.passl: escape(installPath & "/lib/libbacktracenimcpp.a").}
|
2020-06-16 19:09:07 +00:00
|
|
|
else:
|
2024-03-26 19:09:04 +00:00
|
|
|
{.passl: escape(installPath & "/lib/libbacktracenim.a").}
|
2019-12-15 23:21:43 +00:00
|
|
|
|
2020-06-16 19:09:07 +00:00
|
|
|
when defined(libbacktraceUseSystemLibs):
|
|
|
|
{.passl: "-lbacktrace".}
|
|
|
|
when defined(macosx) or defined(windows):
|
|
|
|
{.passl: "-lunwind".}
|
|
|
|
else:
|
2024-03-26 19:09:04 +00:00
|
|
|
{.passc: "-I" & escape(installPath & "/include").}
|
|
|
|
{.passl: escape(installPath & "/lib/libbacktrace.a").}
|
2020-06-16 19:09:07 +00:00
|
|
|
when defined(macosx) or defined(windows):
|
2024-03-26 19:09:04 +00:00
|
|
|
{.passl: escape(installPath & "/lib/libunwind.a").}
|
2019-12-15 23:21:43 +00:00
|
|
|
|
2020-06-16 19:09:07 +00:00
|
|
|
when defined(windows):
|
|
|
|
{.passl: "-lpsapi".}
|
2019-12-15 23:21:43 +00:00
|
|
|
|
2020-03-31 12:50:21 +00:00
|
|
|
proc getBacktrace*(): string {.noinline.} =
|
2020-09-04 00:36:19 +00:00
|
|
|
let
|
|
|
|
# bt: cstring = get_backtrace_c()
|
|
|
|
bt: cstring = get_backtrace_max_length_c(max_length = 128, skip = 3)
|
2020-03-31 12:50:21 +00:00
|
|
|
btLen = len(bt)
|
|
|
|
|
|
|
|
result = newString(btLen)
|
|
|
|
if btLen > 0:
|
|
|
|
copyMem(addr(result[0]), bt, btLen)
|
|
|
|
c_free(bt)
|
|
|
|
|
2020-09-04 00:36:19 +00:00
|
|
|
when defined(nimStackTraceOverride) and declared(registerStackTraceOverride):
|
2023-04-05 16:14:23 +00:00
|
|
|
registerStackTraceOverride(libbacktrace.getBacktrace)
|
2019-12-18 20:24:06 +00:00
|
|
|
|
2020-09-04 00:36:19 +00:00
|
|
|
proc getProgramCounters*(maxLength: cint): seq[cuintptr_t] {.noinline.} =
|
|
|
|
var
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
length {.noinit.}: cint
|
|
|
|
pcPtr = get_program_counters_c(maxLength, addr length, skip = 2)
|
2020-09-04 00:36:19 +00:00
|
|
|
iPtr = pcPtr
|
|
|
|
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
result = newSeqOfCap[cuintptr_t](length)
|
|
|
|
for i in 0 ..< length:
|
|
|
|
if iPtr[] == 0:
|
|
|
|
break
|
2020-09-04 00:36:19 +00:00
|
|
|
result.add(iPtr[])
|
|
|
|
iPtr = cast[ptr cuintptr_t](cast[uint](iPtr) + sizeof(cuintptr_t).uint)
|
|
|
|
|
|
|
|
c_free(pcPtr)
|
|
|
|
|
|
|
|
when defined(nimStackTraceOverride) and declared(registerStackTraceOverrideGetProgramCounters):
|
2023-04-05 16:14:23 +00:00
|
|
|
registerStackTraceOverrideGetProgramCounters(libbacktrace.getProgramCounters)
|
2020-09-04 00:36:19 +00:00
|
|
|
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
proc getDebuggingInfo*(
|
|
|
|
programCounters: seq[cuintptr_t],
|
|
|
|
maxLength: cint): seq[StackTraceEntry] {.noinline.} =
|
|
|
|
doAssert programCounters.len <= cint.high
|
|
|
|
|
2020-09-04 00:36:19 +00:00
|
|
|
if programCounters.len == 0:
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
return @[]
|
2020-09-04 00:36:19 +00:00
|
|
|
|
|
|
|
var
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
length {.noinit.}: cint
|
|
|
|
functionInfoPtr = get_debugging_info_c( # Nim 1.6 needs `unsafeAddr`
|
|
|
|
unsafeAddr programCounters[0], programCounters.len.cint,
|
|
|
|
maxLength, addr length)
|
2020-09-04 00:36:19 +00:00
|
|
|
iPtr = functionInfoPtr
|
|
|
|
res: StackTraceEntry
|
|
|
|
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
result = newSeqOfCap[StackTraceEntry](length.int)
|
|
|
|
for i in 0 ..< length:
|
|
|
|
if iPtr[].filename == nil:
|
|
|
|
break
|
|
|
|
|
2020-09-04 00:36:19 +00:00
|
|
|
# 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
|
|
|
|
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
result.add(res)
|
|
|
|
|
2020-09-04 00:36:19 +00:00
|
|
|
c_free(iPtr[].filename)
|
|
|
|
c_free(iPtr[].function)
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
iPtr = cast[ptr DebuggingInfo](
|
|
|
|
cast[uint](iPtr) + sizeof(DebuggingInfo).uint)
|
2020-09-04 00:36:19 +00:00
|
|
|
|
|
|
|
c_free(functionInfoPtr)
|
|
|
|
|
|
|
|
# Nim convention.
|
|
|
|
reverse(result)
|
|
|
|
|
|
|
|
when defined(nimStackTraceOverride) and declared(registerStackTraceOverrideGetDebuggingInfo):
|
Pass length explicitly instead of relying on NULL terminators (#44)
The `getDebuggingInfo` function relies on `programCounters` being a
NULL terminated list. However, none of the usage actually adds NULL...
- In the path that passes `getProgramCounters` result into
`getDebuggingInfo`, no explicit 0 value is added to `result`.
In practice, there happens to be a 0 there very frequently,
but it is not guaranteed (`env MallocScribble=1`), and even
if it is not there the implementation often continues to work
when processing the extra garbage data, silencing the problem.
- In the path from Nim `addDebuggingInfo` (`system/stacktraces.nim`),
the `programCounters` list is constructed by Nim logic and also
does not add a 0 value to the list. This means that even if we fix
`getProgramCounters` to produce NULL terminated list, other usage
is still broken, and outside the control of this library.
Therefore, remove the NULL terminator logic and pass length explicitly
while retaining any early loop exits when encountering 0 for compat.
Also fix some memory leaks in error conditions.
2024-07-18 15:55:27 +00:00
|
|
|
registerStackTraceOverrideGetDebuggingInfo(libbacktrace.getDebuggingInfo)
|