ORC: added -d:nimOrcStats switch and related API (#23272)

This commit is contained in:
Andreas Rumpf
2024-02-21 16:58:30 +01:00
committed by GitHub
parent 773c066634
commit 59c65009a5
5 changed files with 67 additions and 20 deletions

View File

@@ -30,6 +30,9 @@ slots when enlarging a sequence.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
- Added Viewport API for the JavaScript targets in the `dom` module.
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
instead of `--mm:orc`.
[//]: # "Deprecations:"
@@ -40,7 +43,7 @@ slots when enlarging a sequence.
## Language changes
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.htm#constructor-initializer
- `member` can be used to attach a procedure to a C++ type.
- C++ `constructor` now reuses `result` instead creating `this`.
@@ -62,7 +65,7 @@ slots when enlarging a sequence.
symbols in generic routine bodies to be replaced by symbols injected locally
by templates/macros at instantiation time. `bind` may be used to keep the
captured symbols over the injected ones regardless of enabling the option.
Since this change may affect runtime behavior, the experimental switch
`genericsOpenSym` needs to be enabled, and a warning is given in the case
where an injected symbol would replace a captured symbol not bound by `bind`

View File

@@ -1703,8 +1703,24 @@ when not defined(nimscript):
when not declared(sysFatal):
include "system/fatal"
type
PFrame* = ptr TFrame ## Represents a runtime frame of the call stack;
## part of the debugger API.
# keep in sync with nimbase.h `struct TFrame_`
TFrame* {.importc, nodecl, final.} = object ## The frame itself.
prev*: PFrame ## Previous frame; used for chaining the call stack.
procname*: cstring ## Name of the proc that is currently executing.
line*: int ## Line number of the proc that is currently executing.
filename*: cstring ## Filename of the proc that is currently executing.
len*: int16 ## Length of the inspectable slots.
calldepth*: int16 ## Used for max call depth checking.
when NimStackTraceMsgs:
frameMsgLen*: int ## end position in frameMsgBuf for this frame.
when defined(nimV2):
var
framePtr {.threadvar.}: PFrame
include system/arc
template newException*(exceptn: typedesc, message: string;
@@ -1849,20 +1865,6 @@ when notJSnotNims:
## writes an error message and terminates the program, except when
## using `--os:any`
type
PFrame* = ptr TFrame ## Represents a runtime frame of the call stack;
## part of the debugger API.
# keep in sync with nimbase.h `struct TFrame_`
TFrame* {.importc, nodecl, final.} = object ## The frame itself.
prev*: PFrame ## Previous frame; used for chaining the call stack.
procname*: cstring ## Name of the proc that is currently executing.
line*: int ## Line number of the proc that is currently executing.
filename*: cstring ## Filename of the proc that is currently executing.
len*: int16 ## Length of the inspectable slots.
calldepth*: int16 ## Used for max call depth checking.
when NimStackTraceMsgs:
frameMsgLen*: int ## end position in frameMsgBuf for this frame.
when defined(js) or defined(nimdoc):
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
## Appends `y` to `x` in place.

View File

@@ -26,6 +26,9 @@ else:
rcMask = 0b111
rcShift = 3 # shift by rcShift to get the reference counter
const
orcLeakDetector = defined(nimOrcLeakDetector)
type
RefHeader = object
rc: int # the object header is now a single RC field.
@@ -36,9 +39,21 @@ type
# in O(1) without doubly linked lists
when defined(nimArcDebug) or defined(nimArcIds):
refId: int
when defined(gcOrc) and orcLeakDetector:
filename: cstring
line: int
Cell = ptr RefHeader
template setFrameInfo(c: Cell) =
when orcLeakDetector:
if framePtr != nil and framePtr.prev != nil:
c.filename = framePtr.prev.filename
c.line = framePtr.prev.line
else:
c.filename = nil
c.line = 0
template head(p: pointer): Cell =
cast[Cell](cast[int](p) -% sizeof(RefHeader))
@@ -87,6 +102,7 @@ proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count)
when traceCollector:
cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
setFrameInfo head(result)
proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
# Same as 'newNewObj' but do not initialize the memory to zero.
@@ -109,6 +125,7 @@ proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
when traceCollector:
cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
setFrameInfo head(result)
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
decrement head(p)

View File

@@ -73,8 +73,12 @@ type
when NimStackTraceMsgs:
var frameMsgBuf* {.threadvar.}: string
when not defined(nimV2):
var
framePtr {.threadvar.}: PFrame
var
framePtr {.threadvar.}: PFrame
currException {.threadvar.}: ref Exception
when not gotoBasedExceptions:

View File

@@ -81,10 +81,14 @@ proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
include threadids
when logOrc:
when logOrc or orcLeakDetector:
proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) =
cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n",
msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId())
when orcLeakDetector:
cfprintf(cstderr, "%s %s file: %s:%ld; color: %ld; thread: %ld\n",
msg, desc.name, s.filename, s.line, s.color, getThreadId())
else:
cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n",
msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId())
proc free(s: Cell; desc: PNimTypeV2) {.inline.} =
when traceCollector:
@@ -338,6 +342,8 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
collectColor(s, roots.d[i][1], colToCollect, j)
for i in 0 ..< j.toFree.len:
when orcLeakDetector:
writeCell("CYCLIC OBJECT FREED", j.toFree.d[i][0], j.toFree.d[i][1])
free(j.toFree.d[i][0], j.toFree.d[i][1])
inc j.freed, j.toFree.len
@@ -352,6 +358,9 @@ when defined(nimStressOrc):
else:
var rootsThreshold {.threadvar.}: int
when defined(nimOrcStats):
var freedCyclicObjects {.threadvar.}: int
proc partialCollect(lowMark: int) =
when false:
if roots.len < 10 + lowMark: return
@@ -365,6 +374,8 @@ proc partialCollect(lowMark: int) =
roots.len - lowMark)
roots.len = lowMark
deinit j.traceStack
when defined(nimOrcStats):
inc freedCyclicObjects, j.freed
proc collectCycles() =
## Collect cycles.
@@ -405,6 +416,16 @@ proc collectCycles() =
when logOrc:
cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld rcSum: %ld edges: %ld\n", j.freed, rootsThreshold, j.touched,
getOccupiedMem(), j.rcSum, j.edges)
when defined(nimOrcStats):
inc freedCyclicObjects, j.freed
when defined(nimOrcStats):
type
OrcStats* = object ## Statistics of the cycle collector subsystem.
freedCyclicObjects*: int ## Number of freed cyclic objects.
proc GC_orcStats*(): OrcStats =
## Returns the statistics of the cycle collector subsystem.
result = OrcStats(freedCyclicObjects: freedCyclicObjects)
proc registerCycle(s: Cell; desc: PNimTypeV2) =
s.rootIdx = roots.len+1