added logic to the M&S GC to detect logical leaks
This commit is contained in:
parent
0f3e1f17e9
commit
c3b0eb5df7
2 changed files with 62 additions and 0 deletions
|
|
@ -25,6 +25,13 @@ when defined(nimTypeNames):
|
||||||
c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes)
|
c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes)
|
||||||
it = it.nextType
|
it = it.nextType
|
||||||
|
|
||||||
|
when defined(nimGcRefLeak):
|
||||||
|
proc oomhandler() =
|
||||||
|
c_fprintf(stdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len)
|
||||||
|
writeLeaks()
|
||||||
|
|
||||||
|
outOfMemHook = oomhandler
|
||||||
|
|
||||||
template decTypeSize(cell, t) =
|
template decTypeSize(cell, t) =
|
||||||
# XXX this needs to use atomics for multithreaded apps!
|
# XXX this needs to use atomics for multithreaded apps!
|
||||||
when defined(nimTypeNames):
|
when defined(nimTypeNames):
|
||||||
|
|
|
||||||
|
|
@ -142,11 +142,54 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
|
||||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
|
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
|
||||||
# we need the prototype here for debugging purposes
|
# we need the prototype here for debugging purposes
|
||||||
|
|
||||||
|
when defined(nimGcRefLeak):
|
||||||
|
const
|
||||||
|
MaxTraceLen = 20 # tracking the last 20 calls is enough
|
||||||
|
|
||||||
|
type
|
||||||
|
GcStackTrace = object
|
||||||
|
lines: array[0..MaxTraceLen-1, cstring]
|
||||||
|
files: array[0..MaxTraceLen-1, cstring]
|
||||||
|
|
||||||
|
proc captureStackTrace(f: PFrame, st: var GcStackTrace) =
|
||||||
|
const
|
||||||
|
firstCalls = 5
|
||||||
|
var
|
||||||
|
it = f
|
||||||
|
i = 0
|
||||||
|
total = 0
|
||||||
|
while it != nil and i <= high(st.lines)-(firstCalls-1):
|
||||||
|
# the (-1) is for the "..." entry
|
||||||
|
st.lines[i] = it.procname
|
||||||
|
st.files[i] = it.filename
|
||||||
|
inc(i)
|
||||||
|
inc(total)
|
||||||
|
it = it.prev
|
||||||
|
var b = it
|
||||||
|
while it != nil:
|
||||||
|
inc(total)
|
||||||
|
it = it.prev
|
||||||
|
for j in 1..total-i-(firstCalls-1):
|
||||||
|
if b != nil: b = b.prev
|
||||||
|
if total != i:
|
||||||
|
st.lines[i] = "..."
|
||||||
|
st.files[i] = "..."
|
||||||
|
inc(i)
|
||||||
|
while b != nil and i <= high(st.lines):
|
||||||
|
st.lines[i] = b.procname
|
||||||
|
st.files[i] = b.filename
|
||||||
|
inc(i)
|
||||||
|
b = b.prev
|
||||||
|
|
||||||
|
var ax: array[10_000, GcStackTrace]
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerProc.} =
|
proc nimGCref(p: pointer) {.compilerProc.} =
|
||||||
# we keep it from being collected by pretending it's not even allocated:
|
# we keep it from being collected by pretending it's not even allocated:
|
||||||
when false:
|
when false:
|
||||||
when withBitvectors: excl(gch.allocated, usrToCell(p))
|
when withBitvectors: excl(gch.allocated, usrToCell(p))
|
||||||
else: usrToCell(p).refcount = rcBlack
|
else: usrToCell(p).refcount = rcBlack
|
||||||
|
when defined(nimGcRefLeak):
|
||||||
|
captureStackTrace(framePtr, ax[gch.additionalRoots.len])
|
||||||
add(gch.additionalRoots, usrToCell(p))
|
add(gch.additionalRoots, usrToCell(p))
|
||||||
|
|
||||||
proc nimGCunref(p: pointer) {.compilerProc.} =
|
proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||||
|
|
@ -157,6 +200,8 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
if d[i] == cell:
|
if d[i] == cell:
|
||||||
d[i] = d[L]
|
d[i] = d[L]
|
||||||
|
when defined(nimGcRefLeak):
|
||||||
|
ax[i] = ax[L]
|
||||||
dec gch.additionalRoots.len
|
dec gch.additionalRoots.len
|
||||||
break
|
break
|
||||||
dec(i)
|
dec(i)
|
||||||
|
|
@ -164,6 +209,16 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
|
||||||
when withBitvectors: incl(gch.allocated, usrToCell(p))
|
when withBitvectors: incl(gch.allocated, usrToCell(p))
|
||||||
else: usrToCell(p).refcount = rcWhite
|
else: usrToCell(p).refcount = rcWhite
|
||||||
|
|
||||||
|
when defined(nimGcRefLeak):
|
||||||
|
proc writeLeaks() =
|
||||||
|
for i in 0..gch.additionalRoots.len-1:
|
||||||
|
c_fprintf(stdout, "[Heap] NEW STACK TRACE\n")
|
||||||
|
for ii in 0..MaxTraceLen-1:
|
||||||
|
let line = ax[i].lines[ii]
|
||||||
|
let file = ax[i].files[ii]
|
||||||
|
if isNil(line): break
|
||||||
|
c_fprintf(stdout, "[Heap] %s(%s)\n", file, line)
|
||||||
|
|
||||||
include gc_common
|
include gc_common
|
||||||
|
|
||||||
proc prepareDealloc(cell: PCell) =
|
proc prepareDealloc(cell: PCell) =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue