summary refs log tree commit diff stats
path: root/lib/system
diff options
context:
space:
mode:
Diffstat (limited to 'lib/system')
-rw-r--r--lib/system/alloc.nim6
-rw-r--r--lib/system/excpt.nim2
-rw-r--r--lib/system/gc.nim7
-rw-r--r--lib/system/gc_stack.nim7
-rw-r--r--lib/system/hti.nim1
-rw-r--r--lib/system/memtracker.nim70
-rw-r--r--lib/system/osalloc.nim13
-rw-r--r--lib/system/sysio.nim27
-rw-r--r--lib/system/sysstr.nim51
9 files changed, 139 insertions, 45 deletions
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 745bbbf62..3a8e8a1b6 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -15,6 +15,10 @@
 
 include osalloc
 
+template track(op, address, size) =
+  when defined(memTracker):
+    memTrackerOp(op, address, size)
+
 # We manage *chunks* of memory. Each chunk is a multiple of the page size.
 # Each chunk starts at an address that is divisible by the page size. Chunks
 # that are bigger than ``ChunkOsReturn`` are returned back to the operating
@@ -645,6 +649,7 @@ proc alloc(allocator: var MemRegion, size: Natural): pointer =
   cast[ptr FreeCell](result).zeroField = 1 # mark it as used
   sysAssert(not isAllocatedPtr(allocator, result), "alloc")
   result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
+  track("alloc", result, size)
 
 proc alloc0(allocator: var MemRegion, size: Natural): pointer =
   result = alloc(allocator, size)
@@ -658,6 +663,7 @@ proc dealloc(allocator: var MemRegion, p: pointer) =
   sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc 2")
   rawDealloc(allocator, x)
   sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
+  track("dealloc", p, 0)
 
 proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
   if newsize > 0:
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index dcf41b67d..d00ab64b1 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -339,6 +339,8 @@ when not defined(noSignalHandler):
           action("unknown signal\n")
 
     # print stack trace and quit
+    when defined(memtracker):
+      logPendingOps()
     when hasSomeStackTrace:
       GC_disable()
       var buf = newStringOfCap(2000)
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index c8623f2f1..7fb4c7ac7 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -471,6 +471,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   # its refcount is zero, so add it to the ZCT:
   addNewObjToZCT(res, gch)
   when logGC: writeCell("new cell", res)
+  track("rawNewObj", res, size)
   gcTrace(res, csAllocated)
   release(gch)
   when useCellIds:
@@ -516,6 +517,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
   res.refcount = rcIncrement # refcount is 1
   sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
   when logGC: writeCell("new cell", res)
+  track("newObjRC1", res, size)
   gcTrace(res, csAllocated)
   release(gch)
   when useCellIds:
@@ -558,6 +560,8 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
     writeCell("growObj new cell", res)
   gcTrace(ol, csZctFreed)
   gcTrace(res, csAllocated)
+  track("growObj old", ol, 0)
+  track("growObj new", res, newsize)
   when reallyDealloc:
     sysAssert(allocInv(gch.region), "growObj before dealloc")
     if ol.refcount shr rcShift <=% 1:
@@ -601,6 +605,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
 proc freeCyclicCell(gch: var GcHeap, c: PCell) =
   prepareDealloc(c)
   gcTrace(c, csCycFreed)
+  track("cycle collector dealloc cell", c, 0)
   when logGC: writeCell("cycle collector dealloc cell", c)
   when reallyDealloc:
     sysAssert(allocInv(gch.region), "free cyclic cell")
@@ -670,6 +675,7 @@ proc doOperation(p: pointer, op: WalkOp) =
     gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
     #c.refcount = c.refcount -% rcIncrement
     when logGC: writeCell("decref (from doOperation)", c)
+    track("waZctDecref", p, 0)
     decRef(c)
     #if c.refcount <% rcIncrement: addZCT(gch.zct, c)
   of waPush:
@@ -762,6 +768,7 @@ proc collectZCT(gch: var GcHeap): bool =
       # In any case, it should be removed from the ZCT. But not
       # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
       when logGC: writeCell("zct dealloc cell", c)
+      track("zct dealloc cell", c, 0)
       gcTrace(c, csZctFreed)
       # We are about to free the object, call the finalizer BEFORE its
       # children are deleted as well, because otherwise the finalizer may
diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim
index 5f72b8959..3eda08df9 100644
--- a/lib/system/gc_stack.nim
+++ b/lib/system/gc_stack.nim
@@ -79,6 +79,7 @@ template withRegion*(r: MemRegion; body: untyped) =
   try:
     body
   finally:
+    r = tlRegion
     tlRegion = oldRegion
 
 template inc(p: pointer, s: int) =
@@ -464,4 +465,10 @@ proc getFreeMem(): int = tlRegion.remaining
 proc getTotalMem(): int =
   result = tlRegion.totalSize
 
+proc getOccupiedMem*(r: MemRegion): int =
+  result = r.totalSize - r.remaining
+proc getFreeMem*(r: MemRegion): int = r.remaining
+proc getTotalMem*(r: MemRegion): int =
+  result = r.totalSize
+
 proc setStackBottom(theStackBottom: pointer) = discard
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index 984f888cb..892a209df 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -62,7 +62,6 @@ type
     tyUInt16,
     tyUInt32,
     tyUInt64,
-    tyBigNum,
 
   TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
   TNimNode {.codegenType.} = object
diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim
new file mode 100644
index 000000000..a9767bbca
--- /dev/null
+++ b/lib/system/memtracker.nim
@@ -0,0 +1,70 @@
+#
+#
+#            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.}
+
+type
+  LogEntry* = object
+    op*: cstring
+    address*: pointer
+    size*: int
+    file*: cstring
+    line*: int
+  TrackLog* = object
+    count*: int
+    disabled: bool
+    data*: array[4000, LogEntry]
+  TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], locks: 0.}
+
+var
+  gLog*: TrackLog
+  gLogger*: TrackLogger = proc (log: TrackLog) = discard
+
+proc setTrackLogger*(logger: TrackLogger) =
+  gLogger = logger
+
+proc addEntry(entry: LogEntry) =
+  if not gLog.disabled:
+    if gLog.count > high(gLog.data):
+      gLogger(gLog)
+      gLog.count = 0
+    gLog.data[gLog.count] = entry
+    inc gLog.count
+
+proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerProc.} =
+  addEntry LogEntry(op: "write", address: address,
+      size: size, file: file, line: line)
+
+proc memTrackerOp*(op: cstring; address: pointer; size: int) =
+  addEntry LogEntry(op: op, address: address, size: size,
+      file: "", line: 0)
+
+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
+
+addQuitProc logPendingOps
+
+{.pop.}
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 316dd74d7..b0639a75a 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -81,20 +81,27 @@ elif defined(posix):
   const
     PROT_READ  = 1             # page can be read
     PROT_WRITE = 2             # page can be written
-    MAP_PRIVATE = 2'i32        # Changes are private
 
   when defined(macosx) or defined(bsd):
     const MAP_ANONYMOUS = 0x1000
+    const MAP_PRIVATE = 0x02        # Changes are private
   elif defined(solaris):
     const MAP_ANONYMOUS = 0x100
+    const MAP_PRIVATE = 0x02        # Changes are private
+  elif defined(linux) and defined(amd64):
+    # actually, any architecture using asm-generic, but being conservative here,
+    # some arches like mips and alpha use different values
+    const MAP_ANONYMOUS = 0x20
+    const MAP_PRIVATE = 0x02        # Changes are private
   else:
     var
       MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint
+      MAP_PRIVATE {.importc: "MAP_PRIVATE", header: "<sys/mman.h>".}: cint
 
-  proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
+  proc mmap(adr: pointer, len: csize, prot, flags, fildes: cint,
             off: int): pointer {.header: "<sys/mman.h>".}
 
-  proc munmap(adr: pointer, len: int): cint {.header: "<sys/mman.h>".}
+  proc munmap(adr: pointer, len: csize): cint {.header: "<sys/mman.h>".}
 
   proc osAllocPages(size: int): pointer {.inline.} =
     result = mmap(nil, size, PROT_READ or PROT_WRITE,
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 3e9657ce0..5c10392f1 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -104,21 +104,22 @@ proc getFileHandle*(f: File): FileHandle = c_fileno(f)
 
 proc readLine(f: File, line: var TaintedString): bool =
   var pos = 0
+  var sp: cint = 80
   # Use the currently reserved space for a first try
-  when defined(nimscript):
-    var space: cint = 80
+  if line.string.isNil:
+    line = TaintedString(newStringOfCap(80))
   else:
-    var space: cint = cint(cast[PGenericSeq](line.string).space)
-  line.string.setLen(space)
-
+    when not defined(nimscript):
+      sp = cint(cast[PGenericSeq](line.string).space)
+    line.string.setLen(sp)
   while true:
     # memset to \l so that we can tell how far fgets wrote, even on EOF, where
     # fgets doesn't append an \l
-    c_memset(addr line.string[pos], '\l'.ord, space)
-    if c_fgets(addr line.string[pos], space, f) == nil:
+    c_memset(addr line.string[pos], '\l'.ord, sp)
+    if c_fgets(addr line.string[pos], sp, f) == nil:
       line.string.setLen(0)
       return false
-    let m = c_memchr(addr line.string[pos], '\l'.ord, space)
+    let m = c_memchr(addr line.string[pos], '\l'.ord, sp)
     if m != nil:
       # \l found: Could be our own or the one by fgets, in any case, we're done
       var last = cast[ByteAddress](m) - cast[ByteAddress](addr line.string[0])
@@ -129,17 +130,17 @@ proc readLine(f: File, line: var TaintedString): bool =
         # \0\l\0 => line ending in a null character.
         # \0\l\l => last line without newline, null was put there by fgets.
       elif last > 0 and line.string[last-1] == '\0':
-        if last < pos + space - 1 and line.string[last+1] != '\0':
+        if last < pos + sp - 1 and line.string[last+1] != '\0':
           dec last
       line.string.setLen(last)
       return true
     else:
       # fgets will have inserted a null byte at the end of the string.
-      dec space
+      dec sp
     # No \l found: Increase buffer and read more
-    inc pos, space
-    space = 128 # read in 128 bytes at a time
-    line.string.setLen(pos+space)
+    inc pos, sp
+    sp = 128 # read in 128 bytes at a time
+    line.string.setLen(pos+sp)
 
 proc readLine(f: File): TaintedString =
   result = TaintedString(newStringOfCap(80))
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 3a93221e0..11034006a 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -263,27 +263,32 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
   result.len = newLen
 
 # --------------- other string routines ----------------------------------
-proc nimIntToStr(x: int): string {.compilerRtl.} =
-  result = newString(sizeof(x)*4)
+proc add*(result: var string; x: int64) =
+  let base = result.len
+  setLen(result, base + sizeof(x)*4)
   var i = 0
   var y = x
   while true:
     var d = y div 10
-    result[i] = chr(abs(int(y - d*10)) + ord('0'))
+    result[base+i] = chr(abs(int(y - d*10)) + ord('0'))
     inc(i)
     y = d
     if y == 0: break
   if x < 0:
-    result[i] = '-'
+    result[base+i] = '-'
     inc(i)
-  setLen(result, i)
+  setLen(result, base+i)
   # mirror the string:
   for j in 0..i div 2 - 1:
-    swap(result[j], result[i-j-1])
+    swap(result[base+j], result[base+i-j-1])
 
-proc nimFloatToStr(f: float): string {.compilerproc.} =
+proc nimIntToStr(x: int): string {.compilerRtl.} =
+  result = newStringOfCap(sizeof(x)*4)
+  result.add x
+
+proc add*(result: var string; x: float) =
   var buf: array[0..64, char]
-  var n: int = c_sprintf(buf, "%.16g", f)
+  var n: int = c_sprintf(buf, "%.16g", x)
   var hasDot = false
   for i in 0..n-1:
     if buf[i] == ',':
@@ -298,14 +303,18 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
   # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
   # We want to get rid of these here:
   if buf[n-1] in {'n', 'N'}:
-    result = "nan"
+    result.add "nan"
   elif buf[n-1] == 'F':
     if buf[0] == '-':
-      result = "-inf"
+      result.add "-inf"
     else:
-      result = "inf"
+      result.add "inf"
   else:
-    result = $buf
+    result.add buf
+
+proc nimFloatToStr(f: float): string {.compilerproc.} =
+  result = newStringOfCap(8)
+  result.add f
 
 proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {.
   importc: "strtod", header: "<stdlib.h>", noSideEffect.}
@@ -469,22 +478,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   number = c_strtod(t, nil)
 
 proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
-  result = newString(sizeof(x)*4)
-  var i = 0
-  var y = x
-  while true:
-    var d = y div 10
-    result[i] = chr(abs(int(y - d*10)) + ord('0'))
-    inc(i)
-    y = d
-    if y == 0: break
-  if x < 0:
-    result[i] = '-'
-    inc(i)
-  setLen(result, i)
-  # mirror the string:
-  for j in 0..i div 2 - 1:
-    swap(result[j], result[i-j-1])
+  result = newStringOfCap(sizeof(x)*4)
+  result.add x
 
 proc nimBoolToStr(x: bool): string {.compilerRtl.} =
   return if x: "true" else: "false"