diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2016-02-17 14:52:02 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2016-02-17 14:52:02 +0100 |
commit | 8ec5c01cae61a727159ddfd0ea7c781d10be9f83 (patch) | |
tree | 4dd7c6ea7878c9d4af919d7fada4f0fda09f0b82 | |
parent | 0fa2ed30e0ee5bbe1442ceae01759786a82e85f9 (diff) | |
download | Nim-8ec5c01cae61a727159ddfd0ea7c781d10be9f83.tar.gz |
further progress on --gc:v2
-rw-r--r-- | lib/system.nim | 75 | ||||
-rw-r--r-- | lib/system/alloc.nim | 8 | ||||
-rw-r--r-- | lib/system/ansi_c.nim | 18 | ||||
-rw-r--r-- | lib/system/gc2.nim | 190 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 2 | ||||
-rw-r--r-- | lib/system/sysio.nim | 110 | ||||
-rw-r--r-- | tools/heapdump2dot.nim | 66 |
7 files changed, 321 insertions, 148 deletions
diff --git a/lib/system.nim b/lib/system.nim index 2c049b6b6..152dd54ea 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2589,35 +2589,6 @@ when not defined(JS): #and not defined(nimscript): var strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic}) - when not defined(nimscript): - include "system/ansi_c" - - proc cmp(x, y: string): int = - result = int(c_strcmp(x, y)) - else: - proc cmp(x, y: string): int = - if x < y: result = -1 - elif x > y: result = 1 - else: result = 0 - - const pccHack = if defined(pcc): "_" else: "" # Hack for PCC - when not defined(nimscript): - when defined(windows): - # work-around C's sucking abstraction: - # BUGFIX: stdin and stdout should be binary files! - proc setmode(handle, mode: int) {.importc: pccHack & "setmode", - header: "<io.h>".} - proc fileno(f: C_TextFileStar): int {.importc: pccHack & "fileno", - header: "<fcntl.h>".} - var - O_BINARY {.importc: pccHack & "O_BINARY", nodecl.}: int - - # we use binary mode in Windows: - setmode(fileno(c_stdin), O_BINARY) - setmode(fileno(c_stdout), O_BINARY) - - when defined(endb): - proc endbStep() # ----------------- IO Part ------------------------------------------------ when hostOS != "standalone": @@ -2643,15 +2614,43 @@ when not defined(JS): #and not defined(nimscript): {.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].} - when not defined(nimscript): - # text file handling: + when not defined(nimscript): + include "system/ansi_c" + + proc cmp(x, y: string): int = + result = int(c_strcmp(x, y)) + else: + proc cmp(x, y: string): int = + if x < y: result = -1 + elif x > y: result = 1 + else: result = 0 + + when not defined(nimscript): + when defined(windows): + # work-around C's sucking abstraction: + # BUGFIX: stdin and stdout should be binary files! + proc setmode(handle, mode: int) {.importc: "setmode", + header: "<io.h>".} + proc fileno(f: C_TextFileStar): int {.importc: "fileno", + header: "<fcntl.h>".} var - stdin* {.importc: "stdin", header: "<stdio.h>".}: File - ## The standard input stream. - stdout* {.importc: "stdout", header: "<stdio.h>".}: File - ## The standard output stream. - stderr* {.importc: "stderr", header: "<stdio.h>".}: File - ## The standard error stream. + O_BINARY {.importc: "O_BINARY", nodecl.}: int + + # we use binary mode on Windows: + setmode(fileno(c_stdin), O_BINARY) + setmode(fileno(c_stdout), O_BINARY) + + when defined(endb): + proc endbStep() + + # text file handling: + var + stdin* {.importc: "stdin", header: "<stdio.h>".}: File + ## The standard input stream. + stdout* {.importc: "stdout", header: "<stdio.h>".}: File + ## The standard output stream. + stderr* {.importc: "stderr", header: "<stdio.h>".}: File + ## The standard error stream. when defined(useStdoutAsStdmsg): template stdmsg*: File = stdout @@ -2947,7 +2946,7 @@ when not defined(JS): #and not defined(nimscript): else: include "system/sysio" - when declared(open) and declared(close) and declared(readline): + when not defined(nimscript): iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} = ## Iterates over any line in the file named `filename`. ## diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 6de8e19e7..67d380391 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -592,16 +592,16 @@ proc allocInv(a: MemRegion): bool = ## checks some (not all yet) invariants of the allocator's data structures. for s in low(a.freeSmallChunks)..high(a.freeSmallChunks): var c = a.freeSmallChunks[s] - while c != nil: + while not (c == nil): if c.next == c: echo "[SYSASSERT] c.next == c" return false - if c.size != s * MemAlign: + if not (c.size == s * MemAlign): echo "[SYSASSERT] c.size != s * MemAlign" return false var it = c.freeList - while it != nil: - if it.zeroField != 0: + while not (it == nil): + if not (it.zeroField == 0): echo "[SYSASSERT] it.zeroField != 0" c_printf("%ld %p\n", it.zeroField, it) return false diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 702559034..1bbd89fe7 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -23,14 +23,20 @@ proc c_strlen(a: cstring): int {.header: "<string.h>", proc c_memset(p: pointer, value: cint, size: int) {. header: "<string.h>", importc: "memset".} -type - C_TextFile {.importc: "FILE", header: "<stdio.h>", - final, incompleteStruct.} = object - C_BinaryFile {.importc: "FILE", header: "<stdio.h>", +when not declared(File): + type + C_TextFile {.importc: "FILE", header: "<stdio.h>", final, incompleteStruct.} = object - C_TextFileStar = ptr C_TextFile - C_BinaryFileStar = ptr C_BinaryFile + C_BinaryFile {.importc: "FILE", header: "<stdio.h>", + final, incompleteStruct.} = object + C_TextFileStar = ptr C_TextFile + C_BinaryFileStar = ptr C_BinaryFile +else: + type + C_TextFileStar = File + C_BinaryFileStar = File +type C_JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>".} = object when not defined(vm): diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 6b6b81824..6c44d509e 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -52,7 +52,8 @@ type WalkOp = enum waMarkGlobal, # part of the backup mark&sweep waMarkGrey, - waZctDecRef #, waDebug + waZctDecRef, + waDebug Phase {.pure.} = enum None, Marking, Sweeping @@ -78,7 +79,7 @@ type GcHeap = object # this contains the zero count and # non-zero count table - black: int # either 0 or 1. + black, red: int # either 0 or 1. stack: ptr GcStack stackBottom: pointer phase: Phase @@ -95,6 +96,7 @@ type stat: GcStat additionalRoots: CellSeq # dummy roots for GC_ref/unref spaceIter: ObjectSpaceIter + dumpHeapFile: File # File that is used for GC_dumpHeap var gch {.rtlThreadVar.}: GcHeap @@ -104,6 +106,7 @@ when not defined(useNimRtl): proc initGC() = when not defined(useNimRtl): + gch.red = (1-gch.black) gch.cycleThreshold = InitialCycleThreshold gch.stat.stackScans = 0 gch.stat.cycleCollections = 0 @@ -117,6 +120,14 @@ proc initGC() = init(gch.additionalRoots) init(gch.greyStack) +# Which color to use for new objects is tricky: When we're marking, +# they have to be *white* so that everything is marked that is only +# reachable from them. However, when we are sweeping, they have to +# be black, so that we don't free them prematuredly. In order to save +# a comparison gch.phase == Phase.Marking, we use the pseudo-color +# 'red' for new objects. +template allocColor(): untyped = gch.red + template gcAssert(cond: bool, msg: string) = when defined(useGcAssert): if not cond: @@ -156,15 +167,25 @@ template color(c): expr = c.refCount and colorMask template setColor(c, col) = c.refcount = c.refcount and not colorMask or col -proc writeCell(msg: cstring, c: PCell) = +proc writeCell(file: File; msg: cstring, c: PCell) = var kind = -1 if c.typ != nil: kind = ord(c.typ.kind) + let col = if c.color == rcGrey: 'g' + elif c.color == gch.black: 'b' + else: 'w' + when useCellIds: + let id = c.id + else: + let id = c when leakDetector: - c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n", - msg, c, kind, c.refcount shr rcShift, c.filename, c.line) + c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n", + msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line) else: - c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld; color=%ld\n", - msg, c, kind, c.refcount shr rcShift, c.color) + c_fprintf(file, "%s %p %d rc=%ld color=%c\n", + msg, id, kind, c.refcount shr rcShift, col) + +proc writeCell(msg: cstring, c: PCell) = + c_stdout.writeCell(msg, c) proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.} @@ -236,7 +257,11 @@ proc nimGCunref(p: pointer) {.compilerProc.} = dec(i) template markGrey(x: PCell) = - if x.color == 1-gch.black and gch.phase == Phase.Marking: + if x.color != 1-gch.black and gch.phase == Phase.Marking: + if not isAllocatedPtr(gch.region, x): + c_fprintf(c_stdout, "[GC] markGrey proc: %p\n", x) + #GC_dumpHeap() + sysAssert(false, "wtf") x.setColor(rcGrey) add(gch.greyStack, x) @@ -425,7 +450,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = res.filename = framePtr.prev.filename res.line = framePtr.prev.line # refcount is zero, color is black, but mark it to be in the ZCT - res.refcount = ZctFlag or gch.black + res.refcount = ZctFlag or allocColor() sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") # its refcount is zero, so add it to the ZCT: addNewObjToZCT(res, gch) @@ -468,11 +493,11 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ - when leakDetector and not hasThreadSupport: + when leakDetector: if framePtr != nil and framePtr.prev != nil: res.filename = framePtr.prev.filename res.line = framePtr.prev.line - res.refcount = rcIncrement or gch.black # refcount is 1 + res.refcount = rcIncrement or allocColor() # refcount is 1 sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") when logGC: writeCell("new cell", res) gcTrace(res, csAllocated) @@ -536,7 +561,7 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = # A better fix would be to emit the location specific write barrier for # 'growObj', but this is lots of more work and who knows what new problems # this would create. - res.refcount = rcIncrement or gch.black + res.refcount = rcIncrement or allocColor() decRef(ol) else: sysAssert(ol.typ != nil, "growObj: 5") @@ -581,6 +606,49 @@ template checkTime {.dirty.} = if duration >= gch.maxPause - 50_000: return false +# ---------------- dump heap ---------------- + +proc debugGraph(s: PCell) = + c_fprintf(gch.dumpHeapFile, "child %p\n", s) + +proc dumpRoot(gch: var GcHeap; s: PCell) = + if isAllocatedPtr(gch.region, s): + c_fprintf(gch.dumpHeapFile, "global_root %p\n", s) + else: + c_fprintf(gch.dumpHeapFile, "global_root_invalid %p\n", s) + +proc GC_dumpHeap*(file: File) = + ## Dumps the GCed heap's content to a file. Can be useful for + ## debugging. Produces an undocumented text file format that + ## can be translated into "dot" syntax via the "heapdump2dot" tool. + gch.dumpHeapFile = file + var spaceIter: ObjectSpaceIter + var d = gch.decStack.d + for i in 0 .. < gch.decStack.len: + if isAllocatedPtr(gch.region, d[i]): + c_fprintf(file, "onstack %p\n", d[i]) + else: + c_fprintf(file, "onstack_invalid %p\n", d[i]) + for i in 0 .. < globalMarkersLen: globalMarkers[i]() + while true: + let x = allObjectsAsProc(gch.region, addr spaceIter) + if spaceIter.state < 0: break + if isCell(x): + # cast to PCell is correct here: + var c = cast[PCell](x) + writeCell(file, "cell ", c) + forAllChildren(c, waDebug) + c_fprintf(file, "end\n") + gch.dumpHeapFile = nil + +proc GC_dumpHeap() = + var f: File + if open(f, "heap.txt", fmWrite): + GC_dumpHeap(f) + f.close() + else: + c_fprintf(stdout, "cannot write heap.txt") + # ---------------- cycle collector ------------------------------------------- proc freeCyclicCell(gch: var GcHeap, c: PCell) = @@ -588,6 +656,9 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) = var d = gch.decStack.d for i in 0..gch.decStack.len-1: + if d[i] == c: + writeCell("freeing ", c) + GC_dumpHeap() gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!" prepareDealloc(c) @@ -603,8 +674,8 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) = proc sweep(gch: var GcHeap): bool = takeStartTime(100) #echo "loop start" - let black = gch.black - cfprintf(cstdout, "black is %d\n", black) + let white = 1-gch.black + #cfprintf(cstdout, "black is %d\n", black) while true: let x = allObjectsAsProc(gch.region, addr gch.spaceIter) if gch.spaceIter.state < 0: break @@ -613,7 +684,7 @@ proc sweep(gch: var GcHeap): bool = # cast to PCell is correct here: var c = cast[PCell](x) gcAssert c.color != rcGrey, "cell is still grey?" - if c.color != black: freeCyclicCell(gch, c) + if c.color == white: freeCyclicCell(gch, c) # Since this is incremental, we MUST not set the object to 'white' here. # We could set all the remaining objects to white after the 'sweep' # completed but instead we flip the meaning of black/white to save one @@ -624,11 +695,19 @@ proc sweep(gch: var GcHeap): bool = gch.spaceIter = ObjectSpaceIter() result = true -proc markRoot(gch: var GcHeap, c: PCell) = - # since we start with 'black' cells, we need to mark them here too: - if c.color != rcGrey: +proc markRoot(gch: var GcHeap, c: PCell) {.inline.} = + if c.color == 1-gch.black: c.setColor(rcGrey) add(gch.greyStack, c) + elif c.color == rcGrey: + var isGrey = false + var d = gch.decStack.d + for i in 0..gch.decStack.len-1: + if d[i] == c: + isGrey = true + break + if not isGrey: + gcAssert false, "markRoot: root is already grey?!" proc markIncremental(gch: var GcHeap): bool = var L = addr(gch.greyStack.len) @@ -637,16 +716,30 @@ proc markIncremental(gch: var GcHeap): bool = var c = gch.greyStack.d[0] if not isAllocatedPtr(gch.region, c): c_fprintf(c_stdout, "[GC] not allocated anymore: %p\n", c) + #GC_dumpHeap() + sysAssert(false, "wtf") - sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr") + #sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr") gch.greyStack.d[0] = gch.greyStack.d[L[] - 1] dec(L[]) takeTime() if c.color == rcGrey: c.setColor(gch.black) forAllChildren(c, waMarkGrey) + elif c.color == (1-gch.black): + gcAssert false, "wtf why are there white object in the greystack?" checkTime() gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty " + + # assert that all local roots are black by now: + var d = gch.decStack.d + var errors = false + for i in 0..gch.decStack.len-1: + gcAssert(isAllocatedPtr(gch.region, d[i]), "markIncremental: isAllocatedPtr 2") + if d[i].color != gch.black: + writeCell("not black ", d[i]) + errors = true + gcAssert(not errors, "wtf something wrong hre") result = true proc markGlobals(gch: var GcHeap) = @@ -658,28 +751,6 @@ proc markLocals(gch: var GcHeap) = sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals" markRoot(gch, d[i]) -when logGC: - var - cycleCheckA: array[100, PCell] - cycleCheckALen = 0 - - proc alreadySeen(c: PCell): bool = - for i in 0 .. <cycleCheckALen: - if cycleCheckA[i] == c: return true - if cycleCheckALen == len(cycleCheckA): - gcAssert(false, "cycle detection overflow") - quit 1 - cycleCheckA[cycleCheckALen] = c - inc cycleCheckALen - - proc debugGraph(s: PCell) = - if alreadySeen(s): - writeCell("child cell (already seen) ", s) - else: - writeCell("cell {", s) - forAllChildren(s, waDebug) - c_fprintf(c_stdout, "}\n") - proc doOperation(p: pointer, op: WalkOp) = if p == nil: return var c: PCell = usrToCell(p) @@ -697,17 +768,31 @@ proc doOperation(p: pointer, op: WalkOp) = decRef(c) #if c.refcount <% rcIncrement: addZCT(gch.zct, c) of waMarkGlobal: + template handleRoot = + if gch.dumpHeapFile.isNil: + markRoot(gch, c) + else: + dumpRoot(gch, c) when hasThreadSupport: # could point to a cell which we don't own and don't want to touch/trace - if isAllocatedPtr(gch.region, c): - markRoot(gch, c) + if isAllocatedPtr(gch.region, c): handleRoot() else: - markRoot(gch, c) + #gcAssert(isAllocatedPtr(gch.region, c), "doOperation: waMarkGlobal") + if not isAllocatedPtr(gch.region, c): + c_fprintf(c_stdout, "[GC] not allocated anymore: MarkGlobal %p\n", c) + #GC_dumpHeap() + sysAssert(false, "wtf") + handleRoot() + discard allocInv(gch.region) of waMarkGrey: + if not isAllocatedPtr(gch.region, c): + c_fprintf(c_stdout, "[GC] not allocated anymore: MarkGrey %p\n", c) + #GC_dumpHeap() + sysAssert(false, "wtf") if c.color == 1-gch.black: c.setColor(rcGrey) add(gch.greyStack, c) - #of waDebug: debugGraph(c) + of waDebug: debugGraph(c) proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, WalkOp(op)) @@ -717,20 +802,29 @@ proc collectZCT(gch: var GcHeap): bool {.benign.} proc collectCycles(gch: var GcHeap): bool = # ensure the ZCT 'color' is not used: while gch.zct.len > 0: discard collectZCT(gch) + case gch.phase - of Phase.None, Phase.Marking: - #if gch.phase == Phase.None: + of Phase.None: gch.phase = Phase.Marking markGlobals(gch) + + cfprintf(stdout, "collectCycles: introduced bug E %ld\n", gch.phase) + discard allocInv(gch.region) + of Phase.Marking: + # since locals do not have a write barrier, we need + # to keep re-scanning them :-( but there is really nothing we can + # do about that. markLocals(gch) if markIncremental(gch): gch.phase = Phase.Sweeping + gch.red = 1 - gch.red of Phase.Sweeping: gcAssert gch.greyStack.len == 0, "greystack not empty" if sweep(gch): gch.phase = Phase.None # flip black/white meanings: gch.black = 1 - gch.black + gcAssert gch.red == 1 - gch.black, "red color is wrong" result = true proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = @@ -770,7 +864,7 @@ proc collectZCT(gch: var GcHeap): bool = gch.zct.d[0] = gch.zct.d[L[] - 1] dec(L[]) takeTime() - if c.refcount <% rcIncrement: + if c.refcount <% rcIncrement and c.color != rcGrey: # It may have a RC > 0, if it is in the hardware stack or # it has not been removed yet from the ZCT. This is because # ``incref`` does not bother to remove the cell from the ZCT diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 2e0fecd49..1e85853d1 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -514,7 +514,7 @@ else: include "system/alloc" include "system/cellsets" - when not leakDetector: + when not leakDetector and not useCellIds: sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") when compileOption("gc", "v2"): include "system/gc2" diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 3d0b2aa8a..e81219a70 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -44,9 +44,37 @@ proc memchr(s: pointer, c: cint, n: csize): pointer {. importc: "memchr", header: "<string.h>", tags: [].} proc memset(s: pointer, c: cint, n: csize) {. header: "<string.h>", importc: "memset", tags: [].} +proc fwrite(buf: pointer, size, n: int, f: File): int {. + importc: "fwrite", noDecl.} + +proc raiseEIO(msg: string) {.noinline, noreturn.} = + sysFatal(IOError, msg) {.push stackTrace:off, profiler:off.} +proc readBuffer(f: File, buffer: pointer, len: Natural): int = + result = fread(buffer, 1, len, f) + +proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int = + result = readBuffer(f, addr(a[start]), len) + +proc readChars(f: File, a: var openArray[char], start, len: Natural): int = + result = readBuffer(f, addr(a[start]), len) + proc write(f: File, c: cstring) = fputs(c, f) + +proc writeBuffer(f: File, buffer: pointer, len: Natural): int = + result = fwrite(buffer, 1, len, f) + +proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int = + var x = cast[ptr array[0..1000_000_000, int8]](a) + result = writeBuffer(f, addr(x[start]), len) +proc writeChars(f: File, a: openArray[char], start, len: Natural): int = + var x = cast[ptr array[0..1000_000_000, int8]](a) + result = writeBuffer(f, addr(x[start]), len) + +proc write(f: File, s: string) = + if writeBuffer(f, cstring(s), s.len) != s.len: + raiseEIO("cannot write string to file") {.pop.} when NoFakeVars: @@ -68,9 +96,6 @@ else: const BufSize = 4000 -proc raiseEIO(msg: string) {.noinline, noreturn.} = - sysFatal(IOError, msg) - proc readLine(f: File, line: var TaintedString): bool = var pos = 0 # Use the currently reserved space for a first try @@ -157,6 +182,12 @@ proc rawFileSize(file: File): int = result = ftell(file) discard fseek(file, clong(oldPos), 0) +proc endOfFile(f: File): bool = + # do not blame me; blame the ANSI C standard this is so brain-damaged + var c = fgetc(f) + ungetc(c, f) + return c < 0'i32 + proc readAllFile(file: File, len: int): string = # We acquire the filesize beforehand and hope it doesn't change. # Speeds things up. @@ -188,26 +219,6 @@ proc readAll(file: File): TaintedString = else: result = readAllBuffer(file).TaintedString -proc readFile(filename: string): TaintedString = - var f = open(filename) - try: - result = readAll(f).TaintedString - finally: - close(f) - -proc writeFile(filename, content: string) = - var f = open(filename, fmWrite) - try: - f.write(content) - finally: - close(f) - -proc endOfFile(f: File): bool = - # do not blame me; blame the ANSI C standard this is so brain-damaged - var c = fgetc(f) - ungetc(c, f) - return c < 0'i32 - proc writeLn[Ty](f: File, x: varargs[Ty, `$`]) = for i in items(x): write(f, i) @@ -278,39 +289,12 @@ proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool = result = p != nil proc fdopen(filehandle: FileHandle, mode: cstring): File {. - importc: pccHack & "fdopen", header: "<stdio.h>".} + importc: "fdopen", header: "<stdio.h>".} proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool = f = fdopen(filehandle, FormatOpen[mode]) result = f != nil -proc fwrite(buf: pointer, size, n: int, f: File): int {. - importc: "fwrite", noDecl.} - -proc readBuffer(f: File, buffer: pointer, len: Natural): int = - result = fread(buffer, 1, len, f) - -proc readBytes(f: File, a: var openArray[int8|uint8], start, len: Natural): int = - result = readBuffer(f, addr(a[start]), len) - -proc readChars(f: File, a: var openArray[char], start, len: Natural): int = - result = readBuffer(f, addr(a[start]), len) - -{.push stackTrace:off, profiler:off.} -proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int = - var x = cast[ptr array[0..1000_000_000, int8]](a) - result = writeBuffer(f, addr(x[start]), len) -proc writeChars(f: File, a: openArray[char], start, len: Natural): int = - var x = cast[ptr array[0..1000_000_000, int8]](a) - result = writeBuffer(f, addr(x[start]), len) -proc writeBuffer(f: File, buffer: pointer, len: Natural): int = - result = fwrite(buffer, 1, len, f) - -proc write(f: File, s: string) = - if writeBuffer(f, cstring(s), s.len) != s.len: - raiseEIO("cannot write string to file") -{.pop.} - proc setFilePos(f: File, pos: int64) = if fseek(f, clong(pos), 0) != 0: raiseEIO("cannot set file position") @@ -325,4 +309,28 @@ proc getFileSize(f: File): int64 = result = getFilePos(f) setFilePos(f, oldPos) +when not declared(close): + proc close(f: File) {. + importc: "fclose", header: "<stdio.h>", tags: [].} + +proc readFile(filename: string): TaintedString = + var f: File + if open(f, filename): + try: + result = readAll(f).TaintedString + finally: + close(f) + else: + sysFatal(IOError, "cannot open: ", filename) + +proc writeFile(filename, content: string) = + var f: File + if open(f, filename, fmWrite): + try: + f.write(content) + finally: + close(f) + else: + sysFatal(IOError, "cannot open: ", filename) + {.pop.} diff --git a/tools/heapdump2dot.nim b/tools/heapdump2dot.nim new file mode 100644 index 000000000..4cee6d674 --- /dev/null +++ b/tools/heapdump2dot.nim @@ -0,0 +1,66 @@ + +include prelude + +proc main(input, output: string) = + type NodeKind = enum + local, localInvalid, global, globalInvalid + #c_fprintf(file, "%s %p %d rc=%ld color=%c\n", + # msg, c, kind, c.refcount shr rcShift, col) + # cell 0x10a908190 22 rc=2 color=w + var i, o: File + var roots = initTable[string, NodeKind]() + if open(i, input): + if open(o, output, fmWrite): + o.writeLine("digraph $1 {\n" % extractFilename(input)) + var currNode = "" + for line in lines(i): + let data = line.split() + if data.len == 0: continue + case data[0] + of "end": + currNode = "" + of "cell": + currNode = data[1] + let rc = data[3].substr("rc=".len) + let col = case data[4].substr("color=".len) + of "b": "black" + of "w": "green" + of "g": "grey" + else: "" + o.write("N" & currNode) + if currNode in roots: + let v = roots[currNode] + case v + of local: o.write(" [label=\"local \\N\" fillcolor=$1]" % col) + of localInvalid: o.write(" [label=\"local invalid \\N\" fillcolor=$1]" % col) + of global: o.write(" [label=\"global \\N\" fillcolor=$1]" % col) + of globalInvalid: o.write(" [label=\"global invalid \\N\" fillcolor=$1]" % col) + else: + o.write(" [fillcolor=$1]" % col) + o.writeLine(";") + of "child": + assert currNode.len > 0 + o.writeLine("N$1 -> N$2;" % [currNode, data[1]]) + of "global_root": + roots[data[1]] = global + of "global_root_invalid": + roots[data[1]] = globalInvalid + of "onstack": + roots[data[1]] = local + of "onstack_invalid": + roots[data[1]] = localInvalid + else: discard + close(i) + o.writeLine("\n}") + close(o) + else: + quit "error: cannot open " & output + else: + quit "error: cannot open " & input + +if paramCount() == 1: + main(paramStr(1), changeFileExt(paramStr(1), "dot")) +elif paramCount() == 2: + main(paramStr(1), paramStr(2)) +else: + quit "usage: heapdump2dot inputfile outputfile" |