temporary debugging code for the memory leak investigation
This commit is contained in:
parent
a42545ea3e
commit
c67520a7c5
10 changed files with 125 additions and 25 deletions
|
|
@ -517,7 +517,7 @@ type
|
||||||
TNodeSeq* = seq[PNode]
|
TNodeSeq* = seq[PNode]
|
||||||
PType* = ref TType
|
PType* = ref TType
|
||||||
PSym* = ref TSym
|
PSym* = ref TSym
|
||||||
TNode*{.acyclic, final.} = object # on a 32bit machine, this takes 32 bytes
|
TNode*{.final.} = object # on a 32bit machine, this takes 32 bytes
|
||||||
typ*: PType
|
typ*: PType
|
||||||
comment*: string
|
comment*: string
|
||||||
info*: TLineInfo
|
info*: TLineInfo
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,15 @@ proc getSysType(kind: TTypeKind): PType =
|
||||||
var
|
var
|
||||||
intTypeCache: array[-5..64, PType]
|
intTypeCache: array[-5..64, PType]
|
||||||
|
|
||||||
|
proc resetSysTypes* =
|
||||||
|
systemModule = nil
|
||||||
|
initStrTable(compilerprocs)
|
||||||
|
for i in low(gSysTypes)..high(gSysTypes):
|
||||||
|
gSysTypes[i] = nil
|
||||||
|
|
||||||
|
for i in low(intTypeCache)..high(intTypeCache):
|
||||||
|
intTypeCache[i] = nil
|
||||||
|
|
||||||
proc getIntLitType*(literal: PNode): PType =
|
proc getIntLitType*(literal: PNode): PType =
|
||||||
# we cache some common integer literal types for performance:
|
# we cache some common integer literal types for performance:
|
||||||
let value = literal.intVal
|
let value = literal.intVal
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,23 @@ proc addDep(x: Psym, dep: int32) =
|
||||||
growCache gMemCacheData, dep
|
growCache gMemCacheData, dep
|
||||||
gMemCacheData[x.position].deps.safeAdd(dep)
|
gMemCacheData[x.position].deps.safeAdd(dep)
|
||||||
|
|
||||||
|
proc ResetModule(fileIdx: int32) =
|
||||||
|
echo "HARD RESETTING ", fileIdx.toFilename
|
||||||
|
gMemCacheData[fileIdx].needsRecompile = Yes
|
||||||
|
gCompiledModules[fileIdx] = nil
|
||||||
|
cgendata.gModules[fileIdx] = nil
|
||||||
|
|
||||||
|
proc ResetAllModules =
|
||||||
|
for i in 0..gCompiledModules.high:
|
||||||
|
if gCompiledModules[i] != nil:
|
||||||
|
ResetModule(i.int32)
|
||||||
|
|
||||||
|
for m in cgenModules():
|
||||||
|
echo "CGEN MODULE FOUND"
|
||||||
|
|
||||||
proc checkDepMem(fileIdx: int32): TNeedRecompile =
|
proc checkDepMem(fileIdx: int32): TNeedRecompile =
|
||||||
template markDirty =
|
template markDirty =
|
||||||
echo "HARD RESETTING ", fileIdx.toFilename
|
ResetModule(fileIdx)
|
||||||
gMemCacheData[fileIdx].needsRecompile = Yes
|
|
||||||
gCompiledModules[fileIdx] = nil
|
|
||||||
cgendata.gModules[fileIdx] = nil
|
|
||||||
|
|
||||||
return Yes
|
return Yes
|
||||||
|
|
||||||
if gMemCacheData[fileIdx].needsRecompile != Maybe:
|
if gMemCacheData[fileIdx].needsRecompile != Maybe:
|
||||||
|
|
@ -377,7 +387,13 @@ proc wantMainModule =
|
||||||
if gProjectFull.len == 0:
|
if gProjectFull.len == 0:
|
||||||
Fatal(gCmdLineInfo, errCommandExpectsFilename)
|
Fatal(gCmdLineInfo, errCommandExpectsFilename)
|
||||||
gProjectMainIdx = addFileExt(gProjectFull, nimExt).fileInfoIdx
|
gProjectMainIdx = addFileExt(gProjectFull, nimExt).fileInfoIdx
|
||||||
|
|
||||||
|
var oss: PGenericSeq
|
||||||
|
proc dbgseqimp(x: PGenericSeq) {.cdecl.} =
|
||||||
|
oss = x
|
||||||
|
|
||||||
|
seqdbg = dbgseqimp
|
||||||
|
|
||||||
proc MainCommand =
|
proc MainCommand =
|
||||||
# In "nimrod serve" scenario, each command must reset the registered passes
|
# In "nimrod serve" scenario, each command must reset the registered passes
|
||||||
clearPasses()
|
clearPasses()
|
||||||
|
|
@ -482,6 +498,48 @@ proc MainCommand =
|
||||||
of "e":
|
of "e":
|
||||||
# XXX: temporary command for easier testing
|
# XXX: temporary command for easier testing
|
||||||
commandEval(mainCommandArg())
|
commandEval(mainCommandArg())
|
||||||
|
of "reset":
|
||||||
|
ResetModule(gProjectMainIdx)
|
||||||
|
gcDebugging = true
|
||||||
|
GC_fullCollect()
|
||||||
|
|
||||||
|
resetCompilationLists()
|
||||||
|
ccgutils.resetCaches()
|
||||||
|
ResetAllModules()
|
||||||
|
resetRopeCache()
|
||||||
|
resetSysTypes()
|
||||||
|
gGenericsCache = nil
|
||||||
|
gOwners = @[]
|
||||||
|
rangeDestructorProc = nil
|
||||||
|
|
||||||
|
# XXX: clean these global vars
|
||||||
|
# ccgstmts.gBreakpoints
|
||||||
|
# ccgthreadvars.nimtv
|
||||||
|
# ccgthreadvars.nimtVDeps
|
||||||
|
# ccgthreadvars.nimtvDeclared
|
||||||
|
# cgendata
|
||||||
|
# cgmeth?
|
||||||
|
# condsyms?
|
||||||
|
# depends?
|
||||||
|
# lexer.gLinesCompiled
|
||||||
|
# msgs - error counts
|
||||||
|
# magicsys, when system.nim changes
|
||||||
|
# rodread.rodcompilerProcs
|
||||||
|
# rodread.gTypeTable
|
||||||
|
# rodread.gMods
|
||||||
|
|
||||||
|
# !! ropes.cache
|
||||||
|
# !! semdata.gGenericsCache
|
||||||
|
# semthreads.computed?
|
||||||
|
#
|
||||||
|
# suggest.usageSym
|
||||||
|
#
|
||||||
|
# XXX: can we run out of IDs?
|
||||||
|
# XXX: detect config reloading (implement as error/require restart)
|
||||||
|
# XXX: options are appended (they will accumulate over time)
|
||||||
|
# vis = visimpl
|
||||||
|
GC_fullCollect()
|
||||||
|
echo GC_getStatistics()
|
||||||
of "idetools":
|
of "idetools":
|
||||||
gCmd = cmdIdeTools
|
gCmd = cmdIdeTools
|
||||||
if gEvalExpr != "":
|
if gEvalExpr != "":
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,10 @@ proc freezeMutableRope*(r: PRope) {.inline.} =
|
||||||
var
|
var
|
||||||
cache: array[0..2048*2 -1, PRope]
|
cache: array[0..2048*2 -1, PRope]
|
||||||
|
|
||||||
|
proc resetRopeCache* =
|
||||||
|
for i in low(cache)..high(cache):
|
||||||
|
cache[i] = nil
|
||||||
|
|
||||||
proc RopeInvariant(r: PRope): bool =
|
proc RopeInvariant(r: PRope): bool =
|
||||||
if r == nil:
|
if r == nil:
|
||||||
result = true
|
result = true
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ type
|
||||||
# naming it multiple times
|
# naming it multiple times
|
||||||
|
|
||||||
var
|
var
|
||||||
gGenericsCache: PGenericsCache # save for modularity
|
gGenericsCache*: PGenericsCache # save for modularity
|
||||||
|
|
||||||
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
|
||||||
result.genericSym = s
|
result.genericSym = s
|
||||||
|
|
|
||||||
|
|
@ -933,7 +933,7 @@ var
|
||||||
destructorName = getIdent"destroy_"
|
destructorName = getIdent"destroy_"
|
||||||
destructorParam = getIdent"this_"
|
destructorParam = getIdent"this_"
|
||||||
destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
|
destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
|
||||||
rangeDestructorProc: PSym
|
rangeDestructorProc*: PSym
|
||||||
|
|
||||||
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
|
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
|
||||||
if instantiateDestructor(c, field.typ):
|
if instantiateDestructor(c, field.typ):
|
||||||
|
|
|
||||||
|
|
@ -179,9 +179,9 @@ when not defined(niminheritable):
|
||||||
|
|
||||||
when not defined(EcmaScript) and not defined(NimrodVM):
|
when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
type
|
type
|
||||||
TGenericSeq {.compilerproc, pure, inheritable.} = object
|
TGenericSeq* {.compilerproc, pure, inheritable.} = object
|
||||||
len, reserved: int
|
len, reserved: int
|
||||||
PGenericSeq {.exportc.} = ptr TGenericSeq
|
PGenericSeq* {.exportc.} = ptr TGenericSeq
|
||||||
# len and space without counting the terminating zero:
|
# len and space without counting the terminating zero:
|
||||||
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
|
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
|
||||||
data: array[0..100_000_000, char]
|
data: array[0..100_000_000, char]
|
||||||
|
|
@ -558,7 +558,7 @@ proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
|
||||||
## checking is turned on).
|
## checking is turned on).
|
||||||
|
|
||||||
type
|
type
|
||||||
IntMax32 = int|int8|int16|int32
|
IntMax32 = bool|int|int8|int16|int32
|
||||||
|
|
||||||
proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
|
proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
|
||||||
proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
|
proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ type
|
||||||
TCell {.pure.} = object
|
TCell {.pure.} = object
|
||||||
refcount: int # the refcount and some flags
|
refcount: int # the refcount and some flags
|
||||||
typ: PNimType
|
typ: PNimType
|
||||||
when leakDetector:
|
when trackAllocationSource:
|
||||||
filename: cstring
|
filename: cstring
|
||||||
line: int
|
line: int
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -355,9 +355,10 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||||
var d = cast[TAddress](cellToUsr(cell))
|
var d = cast[TAddress](cellToUsr(cell))
|
||||||
var s = cast[PGenericSeq](d)
|
var s = cast[PGenericSeq](d)
|
||||||
if s != nil:
|
if s != nil:
|
||||||
|
let baseAddr = d +% GenericSeqSize
|
||||||
for i in 0..s.len-1:
|
for i in 0..s.len-1:
|
||||||
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
|
forAllChildrenAux(cast[pointer](baseAddr +% i *% cell.typ.base.size),
|
||||||
GenericSeqSize), cell.typ.base, op)
|
cell.typ.base, op)
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
|
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
|
||||||
|
|
@ -414,10 +415,12 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
|
||||||
sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
sysAssert((cast[TAddress](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 trackAllocationSource and not hasThreadSupport:
|
||||||
if framePtr != nil and framePtr.prev != nil:
|
if framePtr != nil and framePtr.prev != nil and framePtr.prev.prev != nil:
|
||||||
res.filename = framePtr.prev.filename
|
res.filename = framePtr.prev.prev.filename
|
||||||
res.line = framePtr.prev.line
|
res.line = framePtr.prev.prev.line
|
||||||
|
else:
|
||||||
|
res.filename = "nofile"
|
||||||
res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
|
res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
|
||||||
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:
|
||||||
|
|
@ -456,10 +459,12 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
sysAssert((cast[TAddress](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 trackAllocationSource and not hasThreadSupport:
|
||||||
if framePtr != nil and framePtr.prev != nil:
|
if framePtr != nil and framePtr.prev != nil and framePtr.prev.prev != nil:
|
||||||
res.filename = framePtr.prev.filename
|
res.filename = framePtr.prev.prev.filename
|
||||||
res.line = framePtr.prev.line
|
res.line = framePtr.prev.prev.line
|
||||||
|
else:
|
||||||
|
res.filename = "nofile"
|
||||||
res.refcount = rcIncrement # refcount is 1
|
res.refcount = rcIncrement # 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)
|
||||||
|
|
@ -597,6 +602,21 @@ proc collectCycles(gch: var TGcHeap) =
|
||||||
Deinit(gch.cycleRoots)
|
Deinit(gch.cycleRoots)
|
||||||
Init(gch.cycleRoots)
|
Init(gch.cycleRoots)
|
||||||
|
|
||||||
|
var gcDebugging* = false
|
||||||
|
var vis*: proc (a: pointer, b: PNimType)
|
||||||
|
|
||||||
|
proc debugNode(n: ptr TNimNode) =
|
||||||
|
c_fprintf(c_stdout, "node %s\n", n.name)
|
||||||
|
for i in 0..n.len-1:
|
||||||
|
debugNode(n.sons[i])
|
||||||
|
|
||||||
|
proc debugTyp(x: PNimType) =
|
||||||
|
c_fprintf(c_stdout, "type %d\n", x.kind)
|
||||||
|
if x.node != nil:
|
||||||
|
debugNode(x.node)
|
||||||
|
|
||||||
|
var seqdbg* : proc (s: PGenericSeq) {.cdecl.}
|
||||||
|
|
||||||
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
|
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
|
||||||
# the addresses are not as cells on the stack, so turn them to cells:
|
# the addresses are not as cells on the stack, so turn them to cells:
|
||||||
sysAssert(allocInv(gch.region), "gcMark begin")
|
sysAssert(allocInv(gch.region), "gcMark begin")
|
||||||
|
|
@ -607,8 +627,16 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
|
||||||
var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
|
var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
|
||||||
if objStart != nil:
|
if objStart != nil:
|
||||||
# mark the cell:
|
# mark the cell:
|
||||||
objStart.refcount = objStart.refcount +% rcIncrement
|
if gcDebugging:
|
||||||
add(gch.decStack, objStart)
|
c_fprintf(c_stdout, "object root found %d\nfile: %s\nline: %d\n", objStart.typ.kind, objStart.filename, objStart.line)
|
||||||
|
if objStart.typ.kind == tySequence:
|
||||||
|
let sq = cast[PGenericSeq](cellToUsr(objStart))
|
||||||
|
c_fprintf(c_stdout, "seq len: %d\noffset: %ld\n", sq.len, cast[TAddress](p) - cast[TAddress](sq))
|
||||||
|
seqdbg(sq)
|
||||||
|
|
||||||
|
if not gcDebugging:
|
||||||
|
objStart.refcount = objStart.refcount +% rcIncrement
|
||||||
|
add(gch.decStack, objStart)
|
||||||
when false:
|
when false:
|
||||||
if isAllocatedPtr(gch.region, cell):
|
if isAllocatedPtr(gch.region, cell):
|
||||||
sysAssert false, "allocated pointer but not interior?"
|
sysAssert false, "allocated pointer but not interior?"
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ const
|
||||||
alwaysGC = false # collect after every memory allocation (for debugging)
|
alwaysGC = false # collect after every memory allocation (for debugging)
|
||||||
leakDetector = false
|
leakDetector = false
|
||||||
overwriteFree = false
|
overwriteFree = false
|
||||||
|
trackAllocationSource = true or leakDetector
|
||||||
|
|
||||||
cycleGC = true # (de)activate the cycle GC
|
cycleGC = true # (de)activate the cycle GC
|
||||||
reallyDealloc = true # for debugging purposes this can be set to false
|
reallyDealloc = true # for debugging purposes this can be set to false
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue