diff options
Diffstat (limited to 'lib/system')
-rw-r--r-- | lib/system/alloc.nim | 6 | ||||
-rw-r--r-- | lib/system/excpt.nim | 2 | ||||
-rw-r--r-- | lib/system/gc.nim | 7 | ||||
-rw-r--r-- | lib/system/gc_stack.nim | 7 | ||||
-rw-r--r-- | lib/system/hti.nim | 1 | ||||
-rw-r--r-- | lib/system/memtracker.nim | 70 | ||||
-rw-r--r-- | lib/system/osalloc.nim | 13 | ||||
-rw-r--r-- | lib/system/sysio.nim | 27 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 51 |
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" |