1#
2#
3#            Nim's Runtime Library
4#        (c) Copyright 2016 Andreas Rumpf
5#
6#    See the file "copying.txt", included in this
7#    distribution, for details about the copyright.
8#
9
10## Memory tracking support for Nim.
11
12when not defined(memTracker) and not isMainModule:
13  {.error: "Memory tracking support is turned off!".}
14
15{.push memtracker: off.}
16# we import the low level wrapper and are careful not to use Nim's
17# memory manager for anything here.
18import sqlite3
19
20var
21  dbHandle: PSqlite3
22  insertStmt {.threadvar.}: Pstmt
23
24const insertQuery = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
25
26template sbind(x: int; value) =
27  when value is cstring:
28    let ret = insertStmt.bindText(x, value, value.len.int32, SQLITE_TRANSIENT)
29    if ret != SQLITE_OK:
30      quit "could not bind value"
31  else:
32    let ret = insertStmt.bindInt64(x, value)
33    if ret != SQLITE_OK:
34      quit "could not bind value"
35
36when defined(memTracker):
37  proc logEntries(log: TrackLog) {.nimcall, locks: 0, tags: [], gcsafe.} =
38    if insertStmt.isNil:
39      if prepare_v2(dbHandle, insertQuery,
40          insertQuery.len, insertStmt, nil) != SQLITE_OK:
41        quit "could not bind query to insertStmt " & $sqlite3.errmsg(dbHandle)
42    for i in 0..log.count-1:
43      var success = false
44      let e = log.data[i]
45      discard sqlite3.reset(insertStmt)
46      discard clearBindings(insertStmt)
47      sbind 1, e.op
48      sbind(2, cast[int](e.address))
49      sbind 3, e.size
50      sbind 4, e.file
51      sbind 5, e.line
52      if step(insertStmt) == SQLITE_DONE:
53        success = true
54      if not success:
55        quit "could not write to database! " & $sqlite3.errmsg(dbHandle)
56
57proc execQuery(q: string) =
58  var s: Pstmt
59  if prepare_v2(dbHandle, q, q.len.int32, s, nil) == SQLITE_OK:
60    discard step(s)
61    if finalize(s) != SQLITE_OK:
62      quit "could not finalize " & $sqlite3.errmsg(dbHandle)
63  else:
64    quit "could not prepare statement " & $sqlite3.errmsg(dbHandle)
65
66proc setupDb() =
67  execQuery """create table if not exists tracking(
68       id integer primary key,
69       op varchar not null,
70       address integer not null,
71       size integer not null,
72       file varchar not null,
73       line integer not null
74     )"""
75  execQuery "delete from tracking"
76
77if sqlite3.open("memtrack.db", dbHandle) == SQLITE_OK:
78  setupDb()
79  const query = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
80  if prepare_v2(dbHandle, insertQuery,
81      insertQuery.len, insertStmt, nil) == SQLITE_OK:
82    when defined(memTracker):
83      setTrackLogger logEntries
84  else:
85    quit "could not prepare statement B " & $sqlite3.errmsg(dbHandle)
86else:
87  quit "could not setup sqlite " & $sqlite3.errmsg(dbHandle)
88{.pop.}
89