Files
Nim/lib/system/memtracker.nim
Jacek Sieka 2c4b889d0a Remove Nim signal handler for SIGINT (#25169)
Inside a signal handler, you cannot allocate memory because the signal
handler, being implemented with a C
[`signal`](https://en.cppreference.com/w/c/program/signal) call, can be
called _during_ a memory allocation - when that happens, the CTRL-C
handler causes a segfault and/or other inconsistent state.

Similarly, the call can happen from a non-nim thread or inside a C
library function call etc, most of which do not support reentrancy and
therefore cannot be called _from_ a signal handler.

The stack trace facility used in the default handler is unfortunately
beyond fixing without more significant refactoring since it uses
garbage-collected types in its API and implementation.

As an alternative to https://github.com/nim-lang/Nim/pull/25110, this PR
removes the most problematic signal handler, namely the one for SIGINT
(ctrl-c) - SIGINT is special because it's meant to cause a regular
shutdown of the application and crashes during SIGINT handling are both
confusing and, if turned into SIGSEGV, have downstream effects like core
dumps and OS crash reports.

The signal handlers for the various crash scenarios remain as-is - they
may too cause their own crashes but we're already going down in a bad
way, so the harm is more limited - in particular, crashing during a
crash handler corrupts `core`/crash dumps. Users wanting to keep their
core files pristine should continue to use `-d:noSignalHandler` - this
is usually the better option for production applications since they
carry more detail than the Nim stack trace that gets printed.

Finally, the example of a ctrl-c handler performs the same mistake of
calling `echo` which is not well-defined - replace it with an example
that is mostly correct (except maybe for the lack of `volatile` for the
`stop` variable).

(cherry picked from commit 41ce86b577)
2025-09-22 08:47:08 +02:00

107 lines
2.8 KiB
Nim

#
#
# Nim's Runtime Library
# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Memory tracking support for Nim.
when not defined(memTracker):
{.error: "Memory tracking support is turned off! Enable memory tracking by passing `--memtracker:on` to the compiler (see the Nim Compiler User Guide for more options).".}
when defined(noSignalHandler):
{.error: "Memory tracking works better with the default signal handler.".}
# We don't want to memtrack the tracking code ...
{.push memtracker: off.}
when declared(getThreadId):
template myThreadId(): untyped = getThreadId()
else:
template myThreadId(): untyped = 0
type
LogEntry* = object
op*: cstring
address*: pointer
size*: int
file*: cstring
line*: int
thread*: int
TrackLog* = object
count*: int
disabled: bool
data*: array[400, LogEntry]
TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], gcsafe, raises: [].}
var
gLog*: TrackLog
gLogger*: TrackLogger = proc (log: TrackLog) = discard
ilocs: array[4000, (int, int)]
ilocn: int
proc trackLocation*(p: pointer; size: int) =
let x = (cast[int](p), size)
for i in 0..ilocn-1:
# already known?
if ilocs[i] == x: return
ilocs[ilocn] = x
inc ilocn
proc setTrackLogger*(logger: TrackLogger) =
gLogger = logger
proc addEntry(entry: LogEntry) =
if not gLog.disabled:
var interesting = false
for i in 0..ilocn-1:
let p = ilocs[i]
# X..Y and C..D overlap iff (X <= D and C <= Y)
let x = p[0]
let y = p[0]+p[1]-1
let c = cast[int](entry.address)
let d = c + entry.size-1
if x <= d and c <= y:
interesting = myThreadId() != entry.thread # true
break
if interesting:
gLog.disabled = true
cprintf("interesting %s:%ld %s\n", entry.file, entry.line, entry.op)
let x = cast[proc() {.nimcall, tags: [], gcsafe, raises: [].}](writeStackTrace)
x()
rawQuit 1
#if gLog.count > high(gLog.data):
# gLogger(gLog)
# gLog.count = 0
#gLog.data[gLog.count] = entry
#inc gLog.count
#gLog.disabled = false
proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerproc.} =
addEntry LogEntry(op: "write", address: address,
size: size, file: file, line: line, thread: myThreadId())
proc memTrackerOp*(op: cstring; address: pointer; size: int) {.tags: [],
gcsafe.} =
addEntry LogEntry(op: op, address: address, size: size,
file: "", line: 0, thread: myThreadId())
proc memTrackerDisable*() =
gLog.disabled = true
proc memTrackerEnable*() =
gLog.disabled = false
proc logPendingOps() {.noconv.} =
# forward declared and called from Nim's signal handler.
gLogger(gLog)
gLog.count = 0
import std/exitprocs
addExitProc logPendingOps
{.pop.}