summary refs log tree commit diff stats
path: root/lib/system/memtracker.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system/memtracker.nim')
-rw-r--r--lib/system/memtracker.nim106
1 files changed, 106 insertions, 0 deletions
diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim
new file mode 100644
index 000000000..289f4e024
--- /dev/null
+++ b/lib/system/memtracker.nim
@@ -0,0 +1,106 @@
+#
+#
+#            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.}
+
+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.}