further progress on --gc:v2
This commit is contained in:
parent
0fa2ed30e0
commit
8ec5c01cae
7 changed files with 322 additions and 149 deletions
|
|
@ -2589,35 +2589,6 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
var
|
var
|
||||||
strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic})
|
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 ------------------------------------------------
|
# ----------------- IO Part ------------------------------------------------
|
||||||
when hostOS != "standalone":
|
when hostOS != "standalone":
|
||||||
|
|
@ -2643,15 +2614,43 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
|
|
||||||
{.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].}
|
{.deprecated: [TFile: File, TFileHandle: FileHandle, TFileMode: FileMode].}
|
||||||
|
|
||||||
when not defined(nimscript):
|
when not defined(nimscript):
|
||||||
# text file handling:
|
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
|
var
|
||||||
stdin* {.importc: "stdin", header: "<stdio.h>".}: File
|
O_BINARY {.importc: "O_BINARY", nodecl.}: int
|
||||||
## The standard input stream.
|
|
||||||
stdout* {.importc: "stdout", header: "<stdio.h>".}: File
|
# we use binary mode on Windows:
|
||||||
## The standard output stream.
|
setmode(fileno(c_stdin), O_BINARY)
|
||||||
stderr* {.importc: "stderr", header: "<stdio.h>".}: File
|
setmode(fileno(c_stdout), O_BINARY)
|
||||||
## The standard error stream.
|
|
||||||
|
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):
|
when defined(useStdoutAsStdmsg):
|
||||||
template stdmsg*: File = stdout
|
template stdmsg*: File = stdout
|
||||||
|
|
@ -2947,7 +2946,7 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
else:
|
else:
|
||||||
include "system/sysio"
|
include "system/sysio"
|
||||||
|
|
||||||
when declared(open) and declared(close) and declared(readline):
|
when not defined(nimscript):
|
||||||
iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} =
|
iterator lines*(filename: string): TaintedString {.tags: [ReadIOEffect].} =
|
||||||
## Iterates over any line in the file named `filename`.
|
## Iterates over any line in the file named `filename`.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -592,16 +592,16 @@ proc allocInv(a: MemRegion): bool =
|
||||||
## checks some (not all yet) invariants of the allocator's data structures.
|
## checks some (not all yet) invariants of the allocator's data structures.
|
||||||
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
|
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
|
||||||
var c = a.freeSmallChunks[s]
|
var c = a.freeSmallChunks[s]
|
||||||
while c != nil:
|
while not (c == nil):
|
||||||
if c.next == c:
|
if c.next == c:
|
||||||
echo "[SYSASSERT] c.next == c"
|
echo "[SYSASSERT] c.next == c"
|
||||||
return false
|
return false
|
||||||
if c.size != s * MemAlign:
|
if not (c.size == s * MemAlign):
|
||||||
echo "[SYSASSERT] c.size != s * MemAlign"
|
echo "[SYSASSERT] c.size != s * MemAlign"
|
||||||
return false
|
return false
|
||||||
var it = c.freeList
|
var it = c.freeList
|
||||||
while it != nil:
|
while not (it == nil):
|
||||||
if it.zeroField != 0:
|
if not (it.zeroField == 0):
|
||||||
echo "[SYSASSERT] it.zeroField != 0"
|
echo "[SYSASSERT] it.zeroField != 0"
|
||||||
c_printf("%ld %p\n", it.zeroField, it)
|
c_printf("%ld %p\n", it.zeroField, it)
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,20 @@ proc c_strlen(a: cstring): int {.header: "<string.h>",
|
||||||
proc c_memset(p: pointer, value: cint, size: int) {.
|
proc c_memset(p: pointer, value: cint, size: int) {.
|
||||||
header: "<string.h>", importc: "memset".}
|
header: "<string.h>", importc: "memset".}
|
||||||
|
|
||||||
type
|
when not declared(File):
|
||||||
C_TextFile {.importc: "FILE", header: "<stdio.h>",
|
type
|
||||||
final, incompleteStruct.} = object
|
C_TextFile {.importc: "FILE", header: "<stdio.h>",
|
||||||
C_BinaryFile {.importc: "FILE", header: "<stdio.h>",
|
|
||||||
final, incompleteStruct.} = object
|
final, incompleteStruct.} = object
|
||||||
C_TextFileStar = ptr C_TextFile
|
C_BinaryFile {.importc: "FILE", header: "<stdio.h>",
|
||||||
C_BinaryFileStar = ptr C_BinaryFile
|
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
|
C_JmpBuf {.importc: "jmp_buf", header: "<setjmp.h>".} = object
|
||||||
|
|
||||||
when not defined(vm):
|
when not defined(vm):
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ type
|
||||||
WalkOp = enum
|
WalkOp = enum
|
||||||
waMarkGlobal, # part of the backup mark&sweep
|
waMarkGlobal, # part of the backup mark&sweep
|
||||||
waMarkGrey,
|
waMarkGrey,
|
||||||
waZctDecRef #, waDebug
|
waZctDecRef,
|
||||||
|
waDebug
|
||||||
|
|
||||||
Phase {.pure.} = enum
|
Phase {.pure.} = enum
|
||||||
None, Marking, Sweeping
|
None, Marking, Sweeping
|
||||||
|
|
@ -78,7 +79,7 @@ type
|
||||||
|
|
||||||
GcHeap = object # this contains the zero count and
|
GcHeap = object # this contains the zero count and
|
||||||
# non-zero count table
|
# non-zero count table
|
||||||
black: int # either 0 or 1.
|
black, red: int # either 0 or 1.
|
||||||
stack: ptr GcStack
|
stack: ptr GcStack
|
||||||
stackBottom: pointer
|
stackBottom: pointer
|
||||||
phase: Phase
|
phase: Phase
|
||||||
|
|
@ -95,6 +96,7 @@ type
|
||||||
stat: GcStat
|
stat: GcStat
|
||||||
additionalRoots: CellSeq # dummy roots for GC_ref/unref
|
additionalRoots: CellSeq # dummy roots for GC_ref/unref
|
||||||
spaceIter: ObjectSpaceIter
|
spaceIter: ObjectSpaceIter
|
||||||
|
dumpHeapFile: File # File that is used for GC_dumpHeap
|
||||||
|
|
||||||
var
|
var
|
||||||
gch {.rtlThreadVar.}: GcHeap
|
gch {.rtlThreadVar.}: GcHeap
|
||||||
|
|
@ -104,6 +106,7 @@ when not defined(useNimRtl):
|
||||||
|
|
||||||
proc initGC() =
|
proc initGC() =
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
|
gch.red = (1-gch.black)
|
||||||
gch.cycleThreshold = InitialCycleThreshold
|
gch.cycleThreshold = InitialCycleThreshold
|
||||||
gch.stat.stackScans = 0
|
gch.stat.stackScans = 0
|
||||||
gch.stat.cycleCollections = 0
|
gch.stat.cycleCollections = 0
|
||||||
|
|
@ -117,6 +120,14 @@ proc initGC() =
|
||||||
init(gch.additionalRoots)
|
init(gch.additionalRoots)
|
||||||
init(gch.greyStack)
|
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) =
|
template gcAssert(cond: bool, msg: string) =
|
||||||
when defined(useGcAssert):
|
when defined(useGcAssert):
|
||||||
if not cond:
|
if not cond:
|
||||||
|
|
@ -156,15 +167,25 @@ template color(c): expr = c.refCount and colorMask
|
||||||
template setColor(c, col) =
|
template setColor(c, col) =
|
||||||
c.refcount = c.refcount and not colorMask or 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
|
var kind = -1
|
||||||
if c.typ != nil: kind = ord(c.typ.kind)
|
if c.typ != nil: kind = ord(c.typ.kind)
|
||||||
when leakDetector:
|
let col = if c.color == rcGrey: 'g'
|
||||||
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n",
|
elif c.color == gch.black: 'b'
|
||||||
msg, c, kind, c.refcount shr rcShift, c.filename, c.line)
|
else: 'w'
|
||||||
|
when useCellIds:
|
||||||
|
let id = c.id
|
||||||
else:
|
else:
|
||||||
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld; color=%ld\n",
|
let id = c
|
||||||
msg, c, kind, c.refcount shr rcShift, c.color)
|
when leakDetector:
|
||||||
|
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(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.}
|
proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.}
|
||||||
|
|
||||||
|
|
@ -236,7 +257,11 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||||
dec(i)
|
dec(i)
|
||||||
|
|
||||||
template markGrey(x: PCell) =
|
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)
|
x.setColor(rcGrey)
|
||||||
add(gch.greyStack, x)
|
add(gch.greyStack, x)
|
||||||
|
|
||||||
|
|
@ -425,7 +450,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||||
res.filename = framePtr.prev.filename
|
res.filename = framePtr.prev.filename
|
||||||
res.line = framePtr.prev.line
|
res.line = framePtr.prev.line
|
||||||
# refcount is zero, color is black, but mark it to be in the ZCT
|
# 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")
|
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||||
# its refcount is zero, so add it to the ZCT:
|
# its refcount is zero, so add it to the ZCT:
|
||||||
addNewObjToZCT(res, gch)
|
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")
|
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||||
# now it is buffered in the ZCT
|
# now it is buffered in the ZCT
|
||||||
res.typ = typ
|
res.typ = typ
|
||||||
when leakDetector and not hasThreadSupport:
|
when leakDetector:
|
||||||
if framePtr != nil and framePtr.prev != nil:
|
if framePtr != nil and framePtr.prev != nil:
|
||||||
res.filename = framePtr.prev.filename
|
res.filename = framePtr.prev.filename
|
||||||
res.line = framePtr.prev.line
|
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")
|
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||||
when logGC: writeCell("new cell", res)
|
when logGC: writeCell("new cell", res)
|
||||||
gcTrace(res, csAllocated)
|
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
|
# 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
|
# 'growObj', but this is lots of more work and who knows what new problems
|
||||||
# this would create.
|
# this would create.
|
||||||
res.refcount = rcIncrement or gch.black
|
res.refcount = rcIncrement or allocColor()
|
||||||
decRef(ol)
|
decRef(ol)
|
||||||
else:
|
else:
|
||||||
sysAssert(ol.typ != nil, "growObj: 5")
|
sysAssert(ol.typ != nil, "growObj: 5")
|
||||||
|
|
@ -581,6 +606,49 @@ template checkTime {.dirty.} =
|
||||||
if duration >= gch.maxPause - 50_000:
|
if duration >= gch.maxPause - 50_000:
|
||||||
return false
|
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 -------------------------------------------
|
# ---------------- cycle collector -------------------------------------------
|
||||||
|
|
||||||
proc freeCyclicCell(gch: var GcHeap, c: PCell) =
|
proc freeCyclicCell(gch: var GcHeap, c: PCell) =
|
||||||
|
|
@ -588,6 +656,9 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) =
|
||||||
|
|
||||||
var d = gch.decStack.d
|
var d = gch.decStack.d
|
||||||
for i in 0..gch.decStack.len-1:
|
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?!!"
|
gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!"
|
||||||
|
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
|
|
@ -603,8 +674,8 @@ proc freeCyclicCell(gch: var GcHeap, c: PCell) =
|
||||||
proc sweep(gch: var GcHeap): bool =
|
proc sweep(gch: var GcHeap): bool =
|
||||||
takeStartTime(100)
|
takeStartTime(100)
|
||||||
#echo "loop start"
|
#echo "loop start"
|
||||||
let black = gch.black
|
let white = 1-gch.black
|
||||||
cfprintf(cstdout, "black is %d\n", black)
|
#cfprintf(cstdout, "black is %d\n", black)
|
||||||
while true:
|
while true:
|
||||||
let x = allObjectsAsProc(gch.region, addr gch.spaceIter)
|
let x = allObjectsAsProc(gch.region, addr gch.spaceIter)
|
||||||
if gch.spaceIter.state < 0: break
|
if gch.spaceIter.state < 0: break
|
||||||
|
|
@ -613,7 +684,7 @@ proc sweep(gch: var GcHeap): bool =
|
||||||
# cast to PCell is correct here:
|
# cast to PCell is correct here:
|
||||||
var c = cast[PCell](x)
|
var c = cast[PCell](x)
|
||||||
gcAssert c.color != rcGrey, "cell is still grey?"
|
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.
|
# 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'
|
# 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
|
# 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()
|
gch.spaceIter = ObjectSpaceIter()
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc markRoot(gch: var GcHeap, c: PCell) =
|
proc markRoot(gch: var GcHeap, c: PCell) {.inline.} =
|
||||||
# since we start with 'black' cells, we need to mark them here too:
|
if c.color == 1-gch.black:
|
||||||
if c.color != rcGrey:
|
|
||||||
c.setColor(rcGrey)
|
c.setColor(rcGrey)
|
||||||
add(gch.greyStack, c)
|
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 =
|
proc markIncremental(gch: var GcHeap): bool =
|
||||||
var L = addr(gch.greyStack.len)
|
var L = addr(gch.greyStack.len)
|
||||||
|
|
@ -637,16 +716,30 @@ proc markIncremental(gch: var GcHeap): bool =
|
||||||
var c = gch.greyStack.d[0]
|
var c = gch.greyStack.d[0]
|
||||||
if not isAllocatedPtr(gch.region, c):
|
if not isAllocatedPtr(gch.region, c):
|
||||||
c_fprintf(c_stdout, "[GC] not allocated anymore: %p\n", 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]
|
gch.greyStack.d[0] = gch.greyStack.d[L[] - 1]
|
||||||
dec(L[])
|
dec(L[])
|
||||||
takeTime()
|
takeTime()
|
||||||
if c.color == rcGrey:
|
if c.color == rcGrey:
|
||||||
c.setColor(gch.black)
|
c.setColor(gch.black)
|
||||||
forAllChildren(c, waMarkGrey)
|
forAllChildren(c, waMarkGrey)
|
||||||
|
elif c.color == (1-gch.black):
|
||||||
|
gcAssert false, "wtf why are there white object in the greystack?"
|
||||||
checkTime()
|
checkTime()
|
||||||
gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
|
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
|
result = true
|
||||||
|
|
||||||
proc markGlobals(gch: var GcHeap) =
|
proc markGlobals(gch: var GcHeap) =
|
||||||
|
|
@ -658,28 +751,6 @@ proc markLocals(gch: var GcHeap) =
|
||||||
sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals"
|
sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals"
|
||||||
markRoot(gch, d[i])
|
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) =
|
proc doOperation(p: pointer, op: WalkOp) =
|
||||||
if p == nil: return
|
if p == nil: return
|
||||||
var c: PCell = usrToCell(p)
|
var c: PCell = usrToCell(p)
|
||||||
|
|
@ -697,17 +768,31 @@ proc doOperation(p: pointer, op: WalkOp) =
|
||||||
decRef(c)
|
decRef(c)
|
||||||
#if c.refcount <% rcIncrement: addZCT(gch.zct, c)
|
#if c.refcount <% rcIncrement: addZCT(gch.zct, c)
|
||||||
of waMarkGlobal:
|
of waMarkGlobal:
|
||||||
|
template handleRoot =
|
||||||
|
if gch.dumpHeapFile.isNil:
|
||||||
|
markRoot(gch, c)
|
||||||
|
else:
|
||||||
|
dumpRoot(gch, c)
|
||||||
when hasThreadSupport:
|
when hasThreadSupport:
|
||||||
# could point to a cell which we don't own and don't want to touch/trace
|
# could point to a cell which we don't own and don't want to touch/trace
|
||||||
if isAllocatedPtr(gch.region, c):
|
if isAllocatedPtr(gch.region, c): handleRoot()
|
||||||
markRoot(gch, c)
|
|
||||||
else:
|
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:
|
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:
|
if c.color == 1-gch.black:
|
||||||
c.setColor(rcGrey)
|
c.setColor(rcGrey)
|
||||||
add(gch.greyStack, c)
|
add(gch.greyStack, c)
|
||||||
#of waDebug: debugGraph(c)
|
of waDebug: debugGraph(c)
|
||||||
|
|
||||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||||
doOperation(d, WalkOp(op))
|
doOperation(d, WalkOp(op))
|
||||||
|
|
@ -717,20 +802,29 @@ proc collectZCT(gch: var GcHeap): bool {.benign.}
|
||||||
proc collectCycles(gch: var GcHeap): bool =
|
proc collectCycles(gch: var GcHeap): bool =
|
||||||
# ensure the ZCT 'color' is not used:
|
# ensure the ZCT 'color' is not used:
|
||||||
while gch.zct.len > 0: discard collectZCT(gch)
|
while gch.zct.len > 0: discard collectZCT(gch)
|
||||||
|
|
||||||
case gch.phase
|
case gch.phase
|
||||||
of Phase.None, Phase.Marking:
|
of Phase.None:
|
||||||
#if gch.phase == Phase.None:
|
|
||||||
gch.phase = Phase.Marking
|
gch.phase = Phase.Marking
|
||||||
markGlobals(gch)
|
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)
|
markLocals(gch)
|
||||||
if markIncremental(gch):
|
if markIncremental(gch):
|
||||||
gch.phase = Phase.Sweeping
|
gch.phase = Phase.Sweeping
|
||||||
|
gch.red = 1 - gch.red
|
||||||
of Phase.Sweeping:
|
of Phase.Sweeping:
|
||||||
gcAssert gch.greyStack.len == 0, "greystack not empty"
|
gcAssert gch.greyStack.len == 0, "greystack not empty"
|
||||||
if sweep(gch):
|
if sweep(gch):
|
||||||
gch.phase = Phase.None
|
gch.phase = Phase.None
|
||||||
# flip black/white meanings:
|
# flip black/white meanings:
|
||||||
gch.black = 1 - gch.black
|
gch.black = 1 - gch.black
|
||||||
|
gcAssert gch.red == 1 - gch.black, "red color is wrong"
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
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]
|
gch.zct.d[0] = gch.zct.d[L[] - 1]
|
||||||
dec(L[])
|
dec(L[])
|
||||||
takeTime()
|
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 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
|
# it has not been removed yet from the ZCT. This is because
|
||||||
# ``incref`` does not bother to remove the cell from the ZCT
|
# ``incref`` does not bother to remove the cell from the ZCT
|
||||||
|
|
|
||||||
|
|
@ -514,7 +514,7 @@ else:
|
||||||
include "system/alloc"
|
include "system/alloc"
|
||||||
|
|
||||||
include "system/cellsets"
|
include "system/cellsets"
|
||||||
when not leakDetector:
|
when not leakDetector and not useCellIds:
|
||||||
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
||||||
when compileOption("gc", "v2"):
|
when compileOption("gc", "v2"):
|
||||||
include "system/gc2"
|
include "system/gc2"
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,37 @@ proc memchr(s: pointer, c: cint, n: csize): pointer {.
|
||||||
importc: "memchr", header: "<string.h>", tags: [].}
|
importc: "memchr", header: "<string.h>", tags: [].}
|
||||||
proc memset(s: pointer, c: cint, n: csize) {.
|
proc memset(s: pointer, c: cint, n: csize) {.
|
||||||
header: "<string.h>", importc: "memset", tags: [].}
|
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.}
|
{.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 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.}
|
{.pop.}
|
||||||
|
|
||||||
when NoFakeVars:
|
when NoFakeVars:
|
||||||
|
|
@ -68,9 +96,6 @@ else:
|
||||||
const
|
const
|
||||||
BufSize = 4000
|
BufSize = 4000
|
||||||
|
|
||||||
proc raiseEIO(msg: string) {.noinline, noreturn.} =
|
|
||||||
sysFatal(IOError, msg)
|
|
||||||
|
|
||||||
proc readLine(f: File, line: var TaintedString): bool =
|
proc readLine(f: File, line: var TaintedString): bool =
|
||||||
var pos = 0
|
var pos = 0
|
||||||
# Use the currently reserved space for a first try
|
# Use the currently reserved space for a first try
|
||||||
|
|
@ -157,6 +182,12 @@ proc rawFileSize(file: File): int =
|
||||||
result = ftell(file)
|
result = ftell(file)
|
||||||
discard fseek(file, clong(oldPos), 0)
|
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 =
|
proc readAllFile(file: File, len: int): string =
|
||||||
# We acquire the filesize beforehand and hope it doesn't change.
|
# We acquire the filesize beforehand and hope it doesn't change.
|
||||||
# Speeds things up.
|
# Speeds things up.
|
||||||
|
|
@ -188,26 +219,6 @@ proc readAll(file: File): TaintedString =
|
||||||
else:
|
else:
|
||||||
result = readAllBuffer(file).TaintedString
|
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, `$`]) =
|
proc writeLn[Ty](f: File, x: varargs[Ty, `$`]) =
|
||||||
for i in items(x):
|
for i in items(x):
|
||||||
write(f, i)
|
write(f, i)
|
||||||
|
|
@ -278,39 +289,12 @@ proc reopen(f: File, filename: string, mode: FileMode = fmRead): bool =
|
||||||
result = p != nil
|
result = p != nil
|
||||||
|
|
||||||
proc fdopen(filehandle: FileHandle, mode: cstring): File {.
|
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 =
|
proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool =
|
||||||
f = fdopen(filehandle, FormatOpen[mode])
|
f = fdopen(filehandle, FormatOpen[mode])
|
||||||
result = f != nil
|
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) =
|
proc setFilePos(f: File, pos: int64) =
|
||||||
if fseek(f, clong(pos), 0) != 0:
|
if fseek(f, clong(pos), 0) != 0:
|
||||||
raiseEIO("cannot set file position")
|
raiseEIO("cannot set file position")
|
||||||
|
|
@ -325,4 +309,28 @@ proc getFileSize(f: File): int64 =
|
||||||
result = getFilePos(f)
|
result = getFilePos(f)
|
||||||
setFilePos(f, oldPos)
|
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.}
|
{.pop.}
|
||||||
|
|
|
||||||
66
tools/heapdump2dot.nim
Normal file
66
tools/heapdump2dot.nim
Normal file
|
|
@ -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"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue