mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-13 06:43:52 +00:00
pragma on/off improvements; endb rewritten
This commit is contained in:
@@ -662,7 +662,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
|
||||
[rdCharLoc(b), first, intLiteral(lastOrd(ty))])
|
||||
if d.k == locNone: d.s = a.s
|
||||
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)),
|
||||
ropef("$1[($2)-$3]", [rdLoc(a), rdCharLoc(b), first]))
|
||||
ropef("$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]))
|
||||
|
||||
proc genCStringElem(p: BProc, e: PNode, d: var TLoc) =
|
||||
var a, b: TLoc
|
||||
@@ -691,7 +691,7 @@ proc genSeqElem(p: BPRoc, e: PNode, d: var TLoc) =
|
||||
var ty = skipTypes(a.t, abstractVarRange)
|
||||
if ty.kind in {tyRef, tyPtr}:
|
||||
ty = skipTypes(ty.sons[0], abstractVarRange) # emit range check:
|
||||
if (optBoundsCheck in p.options):
|
||||
if optBoundsCheck in p.options:
|
||||
if ty.kind == tyString:
|
||||
appcg(p, cpsStmts,
|
||||
"if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
|
||||
|
||||
@@ -615,6 +615,15 @@ proc genBreakPoint(p: BProc, t: PNode) =
|
||||
toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)),
|
||||
makeCString(name)])
|
||||
|
||||
proc genWatchpoint(p: BProc, n: PNode) =
|
||||
if optEndb notin p.Options: return
|
||||
var a: TLoc
|
||||
initLocExpr(p, n.sons[1], a)
|
||||
let typ = skipTypes(n.sons[1].typ, abstractVarRange)
|
||||
appcg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
|
||||
[a.addrLoc, makeCString(renderTree(n.sons[1])),
|
||||
genTypeInfo(p.module, typ)])
|
||||
|
||||
proc genPragma(p: BProc, n: PNode) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
@@ -628,6 +637,8 @@ proc genPragma(p: BProc, n: PNode) =
|
||||
# we need to keep track of ``deadCodeElim`` pragma
|
||||
if (sfDeadCodeElim in p.module.module.flags):
|
||||
addPendingModule(p.module)
|
||||
of wWatchpoint:
|
||||
genWatchpoint(p, it)
|
||||
else: nil
|
||||
|
||||
proc FieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool =
|
||||
|
||||
@@ -37,7 +37,7 @@ const
|
||||
wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
|
||||
wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
|
||||
wBreakpoint, wCheckpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
|
||||
wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd}
|
||||
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||
@@ -59,17 +59,16 @@ const
|
||||
allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
|
||||
|
||||
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
|
||||
proc pragmaAsm*(c: PContext, n: PNode): char
|
||||
# implementation
|
||||
|
||||
proc invalidPragma(n: PNode) =
|
||||
LocalError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
|
||||
|
||||
proc pragmaAsm(c: PContext, n: PNode): char =
|
||||
proc pragmaAsm*(c: PContext, n: PNode): char =
|
||||
result = '\0'
|
||||
if n != nil:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
let it = n.sons[i]
|
||||
if (it.kind == nkExprColonExpr) and (it.sons[0].kind == nkIdent):
|
||||
case whichKeyword(it.sons[0].ident)
|
||||
of wSubsChar:
|
||||
@@ -155,13 +154,11 @@ proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
|
||||
result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall))
|
||||
|
||||
proc IsTurnedOn(c: PContext, n: PNode): bool =
|
||||
if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
|
||||
case whichKeyword(n.sons[1].ident)
|
||||
of wOn: result = true
|
||||
of wOff: result = false
|
||||
else: LocalError(n.info, errOnOrOffExpected)
|
||||
else:
|
||||
LocalError(n.info, errOnOrOffExpected)
|
||||
if n.kind == nkExprColonExpr:
|
||||
let x = c.semConstBoolExpr(c, n.sons[1])
|
||||
n.sons[1] = x
|
||||
if x.kind == nkIntLit: return x.intVal != 0
|
||||
LocalError(n.info, errOnOrOffExpected)
|
||||
|
||||
proc onOff(c: PContext, n: PNode, op: TOptions) =
|
||||
if IsTurnedOn(c, n): gOptions = gOptions + op
|
||||
@@ -211,28 +208,29 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
|
||||
else:
|
||||
incl(sym.loc.flags, lfExportLib)
|
||||
|
||||
proc processNote(c: PContext, n: PNode) =
|
||||
proc processNote(c: PContext, n: PNode) =
|
||||
if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
|
||||
(n.sons[0].kind == nkBracketExpr) and
|
||||
(n.sons[0].sons[1].kind == nkIdent) and
|
||||
(n.sons[0].sons[0].kind == nkIdent) and (n.sons[1].kind == nkIdent):
|
||||
(n.sons[0].sons[0].kind == nkIdent) and (n.sons[1].kind == nkIdent):
|
||||
var nk: TNoteKind
|
||||
case whichKeyword(n.sons[0].sons[0].ident)
|
||||
of wHint:
|
||||
of wHint:
|
||||
var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
|
||||
if x >= 0: nk = TNoteKind(x + ord(hintMin))
|
||||
else: invalidPragma(n)
|
||||
of wWarning:
|
||||
of wWarning:
|
||||
var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
|
||||
if x >= 0: nk = TNoteKind(x + ord(warnMin))
|
||||
else: InvalidPragma(n)
|
||||
else:
|
||||
else:
|
||||
invalidPragma(n)
|
||||
return
|
||||
case whichKeyword(n.sons[1].ident)
|
||||
of wOn: incl(gNotes, nk)
|
||||
of wOff: excl(gNotes, nk)
|
||||
else: LocalError(n.info, errOnOrOffExpected)
|
||||
return
|
||||
|
||||
let x = c.semConstBoolExpr(c, n.sons[1])
|
||||
n.sons[1] = x
|
||||
if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk)
|
||||
else: excl(gNotes, nk)
|
||||
else:
|
||||
invalidPragma(n)
|
||||
|
||||
@@ -335,8 +333,8 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
|
||||
if found == "": found = f # use the default
|
||||
case feature
|
||||
of linkNormal: extccomp.addFileToLink(found)
|
||||
of linkSys:
|
||||
extccomp.addFileToLink(joinPath(libpath, completeCFilePath(found, false)))
|
||||
of linkSys:
|
||||
extccomp.addFileToLink(libpath / completeCFilePath(found, false))
|
||||
else: internalError(n.info, "processCommonLink")
|
||||
|
||||
proc PragmaBreakpoint(c: PContext, n: PNode) =
|
||||
@@ -348,6 +346,12 @@ proc PragmaCheckpoint(c: PContext, n: PNode) =
|
||||
inc(info.line) # next line is affected!
|
||||
msgs.addCheckpoint(info)
|
||||
|
||||
proc PragmaWatchpoint(c: PContext, n: PNode) =
|
||||
if n.kind == nkExprColonExpr:
|
||||
n.sons[1] = c.semExpr(c, n.sons[1])
|
||||
else:
|
||||
invalidPragma(n)
|
||||
|
||||
proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||
case n.sons[1].kind
|
||||
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||
@@ -562,7 +566,7 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||
of wPassL: extccomp.addLinkOption(expectStrLit(c, it))
|
||||
of wPassC: extccomp.addCompileOption(expectStrLit(c, it))
|
||||
of wBreakpoint: PragmaBreakpoint(c, it)
|
||||
of wCheckpoint: PragmaCheckpoint(c, it)
|
||||
of wWatchpoint: PragmaWatchpoint(c, it)
|
||||
of wPush:
|
||||
processPush(c, n, i + 1)
|
||||
break
|
||||
|
||||
@@ -322,6 +322,7 @@ proc lsons(n: PNode, start: int = 0, theEnd: int = - 1): int =
|
||||
|
||||
proc lsub(n: PNode): int =
|
||||
# computes the length of a tree
|
||||
if isNil(n): return 0
|
||||
if n.comment != nil: return maxLineLen + 1
|
||||
case n.kind
|
||||
of nkEmpty: result = 0
|
||||
@@ -663,6 +664,7 @@ proc gident(g: var TSrcGen, n: PNode) =
|
||||
if n.kind == nkSym and renderIds in g.flags: put(g, tkIntLit, $n.sym.id)
|
||||
|
||||
proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
if isNil(n): return
|
||||
var
|
||||
L: int
|
||||
a: TContext
|
||||
|
||||
@@ -160,6 +160,7 @@ proc myOpen(module: PSym, filename: string): PPassContext =
|
||||
if (c.p != nil): InternalError(module.info, "sem.myOpen")
|
||||
c.semConstExpr = semConstExpr
|
||||
c.semExpr = semExprNoFlags
|
||||
c.semConstBoolExpr = semConstBoolExpr
|
||||
pushProcCon(c, module)
|
||||
pushOwner(c.module)
|
||||
openScope(c.tab) # scope for imported symbols
|
||||
|
||||
@@ -67,6 +67,7 @@ type
|
||||
libs*: TLinkedList # all libs used by this module
|
||||
semConstExpr*: proc (c: PContext, n: PNode): PNode # for the pragmas
|
||||
semExpr*: proc (c: PContext, n: PNode): PNode # for the pragmas
|
||||
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode # XXX bite the bullet
|
||||
includedFiles*: TIntSet # used to detect recursive include files
|
||||
filename*: string # the module's filename
|
||||
userPragmas*: TStrTable
|
||||
|
||||
@@ -17,7 +17,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
# the correct branch. Otherwise the AST will be passed through semStmt.
|
||||
result = nil
|
||||
|
||||
template set_result(e: expr) =
|
||||
template setResult(e: expr) =
|
||||
if semCheck: result = semStmt(c, e) # do not open a new scope!
|
||||
else: result = e
|
||||
|
||||
@@ -27,17 +27,17 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
|
||||
of nkElifBranch:
|
||||
checkSonsLen(it, 2)
|
||||
var e = semAndEvalConstExpr(c, it.sons[0])
|
||||
if (e.kind != nkIntLit): InternalError(n.info, "semWhen")
|
||||
if (e.intVal != 0) and (result == nil):
|
||||
set_result(it.sons[1])
|
||||
of nkElse:
|
||||
if e.kind != nkIntLit: InternalError(n.info, "semWhen")
|
||||
if e.intVal != 0 and result == nil:
|
||||
setResult(it.sons[1])
|
||||
of nkElse:
|
||||
checkSonsLen(it, 1)
|
||||
if result == nil:
|
||||
set_result(it.sons[0])
|
||||
setResult(it.sons[0])
|
||||
else: illFormedAst(n)
|
||||
if result == nil:
|
||||
result = newNodeI(nkNilLit, n.info)
|
||||
# The ``when`` statement implements the mechanism for platform dependant
|
||||
# The ``when`` statement implements the mechanism for platform dependent
|
||||
# code. Thus we try to ensure here consistent ID allocation after the
|
||||
# ``when`` statement.
|
||||
IDsynchronizationPoint(200)
|
||||
|
||||
@@ -125,8 +125,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
var a = semConstExpr(c, n[1])
|
||||
var b = semConstExpr(c, n[2])
|
||||
if not sameType(a.typ, b.typ): GlobalError(n.info, errPureTypeMismatch)
|
||||
if not (a.typ.kind in
|
||||
{tyInt..tyInt64, tyEnum, tyBool, tyChar, tyFloat..tyFloat128}):
|
||||
if a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar,tyFloat..tyFloat128}:
|
||||
GlobalError(n.info, errOrdinalTypeExpected)
|
||||
if enumHasHoles(a.typ):
|
||||
GlobalError(n.info, errEnumXHasHoles, a.typ.sym.name.s)
|
||||
|
||||
@@ -53,7 +53,7 @@ type
|
||||
wCompileTime, wNoInit,
|
||||
wPassc, wPassl, wBorrow, wDiscardable,
|
||||
wFieldChecks,
|
||||
wCheckPoint, wSubsChar,
|
||||
wWatchPoint, wSubsChar,
|
||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
||||
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wNoStackFrame
|
||||
|
||||
@@ -98,7 +98,7 @@ const
|
||||
"pragma",
|
||||
"compiletime", "noinit",
|
||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||
"checkpoint",
|
||||
"watchpoint",
|
||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
|
||||
"nostackframe"]
|
||||
|
||||
35
doc/endb.txt
35
doc/endb.txt
@@ -8,10 +8,7 @@
|
||||
.. contents::
|
||||
|
||||
|
||||
**Note:** ENDB has not been maintained/tested since several versions. Help if
|
||||
you want this debugger to survive.
|
||||
|
||||
Nimrod comes with a platform independant debugger -
|
||||
Nimrod comes with a platform independent debugger -
|
||||
the `Embedded Nimrod Debugger`:idx: (`ENDB`:idx:). The debugger is
|
||||
*embedded* into your executable if it has been
|
||||
compiled with the ``--debugger:on`` command line option.
|
||||
@@ -102,8 +99,8 @@ and again because they are lost when you restart your program. This is not
|
||||
necessary: A special pragma has been defined for this:
|
||||
|
||||
|
||||
The ``{.breakpoint.}`` pragma
|
||||
-----------------------------
|
||||
The ``breakpoint`` pragma
|
||||
-------------------------
|
||||
|
||||
The `breakpoint`:idx: pragma is syntactically a statement. It can be used
|
||||
to mark the *following line* as a breakpoint:
|
||||
@@ -122,6 +119,32 @@ is turned on, so you don't need to remove it from your source code after
|
||||
debugging.
|
||||
|
||||
|
||||
The ``watchpoint`` pragma
|
||||
-------------------------
|
||||
|
||||
The `watchpoint`:idx: pragma is syntactically a statement. It can be used
|
||||
to mark a location as a watchpoint:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
var a: array [0..20, int]
|
||||
|
||||
{.watchpoint: a[3].}
|
||||
for i in 0 .. 20: a[i] = i
|
||||
|
||||
ENDB then writes a stack trace whenever the content of the location ``a[3]``
|
||||
changes. The current implementation only tracks a hash value of the location's
|
||||
contents and so locations that are not word sized may encounter false
|
||||
negatives in very rare cases.
|
||||
|
||||
Code for the ``watchpoint`` pragma is only generated if the debugger
|
||||
is turned on, so you don't need to remove it from your source code after
|
||||
debugging.
|
||||
|
||||
Due to the primitive implementation watchpoints are even slower than
|
||||
breakpoints: After *every* executed Nimrod code line it is checked whether the
|
||||
location changed.
|
||||
|
||||
|
||||
Data Display Commands
|
||||
=====================
|
||||
|
||||
|
||||
@@ -35,12 +35,14 @@ proc reverse*[T](a: var openArray[T]) =
|
||||
reverse(a, 0, a.high)
|
||||
|
||||
const
|
||||
onlySafeCode = false
|
||||
onlySafeCode = true
|
||||
|
||||
proc merge[T](a, b: var openArray[T], lo, m, hi: int,
|
||||
cmp: proc (x, y: T): int, order: TSortOrder) =
|
||||
template `<-` (a, b: expr) =
|
||||
when onlySafeCode:
|
||||
when true:
|
||||
a = b
|
||||
elif onlySafeCode:
|
||||
shallowCopy(a, b)
|
||||
else:
|
||||
copyMem(addr(a), addr(b), sizeof(T))
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
## explicitly. Because of this there cannot be a user-defined module named
|
||||
## ``system``.
|
||||
|
||||
{.push hints: off.}
|
||||
|
||||
type
|
||||
int* {.magic: Int.} ## default integer type; bitwidth depends on
|
||||
## architecture, but is always the same as a pointer
|
||||
@@ -35,9 +33,15 @@ type
|
||||
string* {.magic: String.} ## built-in string type
|
||||
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
|
||||
pointer* {.magic: Pointer.} ## built-in pointer type
|
||||
Ordinal* {.magic: Ordinal.}[T]
|
||||
|
||||
const
|
||||
on* = true ## alias for ``true``
|
||||
off* = false ## alias for ``false``
|
||||
|
||||
{.push hints: off.}
|
||||
|
||||
type
|
||||
Ordinal* {.magic: Ordinal.}[T]
|
||||
`nil` {.magic: "Nil".}
|
||||
expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
|
||||
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
|
||||
@@ -831,10 +835,16 @@ proc quit*(errorcode: int = QuitSuccess) {.
|
||||
## It does *not* call the garbage collector to free all the memory,
|
||||
## unless a quit procedure calls ``GC_collect``.
|
||||
|
||||
proc WriteStackTrace()
|
||||
|
||||
var checkDisabled: bool
|
||||
|
||||
template sysAssert(cond, msg: expr) =
|
||||
when defined(useSysAssert):
|
||||
if not cond:
|
||||
if not checkDisabled and not cond:
|
||||
checkDisabled = true
|
||||
echo "[SYSASSERT] ", msg
|
||||
WriteStackTrace()
|
||||
quit 1
|
||||
nil
|
||||
|
||||
@@ -1124,8 +1134,10 @@ proc `$` *[T](x: ordinal[T]): string {.magic: "EnumToStr", noSideEffect.}
|
||||
|
||||
# undocumented:
|
||||
proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.}
|
||||
proc getRefcount*(x: string): int {.importc: "getRefcount", noSideEffect.}
|
||||
proc getRefcount*[T](x: seq[T]): int {.importc: "getRefcount", noSideEffect.}
|
||||
## retrieves the reference count of an heap-allocated object. The
|
||||
## value is implementation-dependant.
|
||||
## value is implementation-dependent.
|
||||
|
||||
# new constants:
|
||||
const
|
||||
@@ -1459,7 +1471,7 @@ template accumulateResult*(iter: expr) =
|
||||
# we have to compute this here before turning it off in except.nim anyway ...
|
||||
const nimrodStackTrace = compileOption("stacktrace")
|
||||
|
||||
{.push checks: off, debugger: off.}
|
||||
{.push checks: off.}
|
||||
# obviously we cannot generate checking operations here :-)
|
||||
# because it would yield into an endless recursion
|
||||
# however, stack-traces are available for most parts
|
||||
@@ -1516,8 +1528,8 @@ type
|
||||
len: int # length of slots (when not debugging always zero)
|
||||
|
||||
when not defined(ECMAScript):
|
||||
{.push stack_trace:off}
|
||||
proc add*(x: var string, y: cstring) =
|
||||
{.push stack_trace:off.}
|
||||
proc add*(x: var string, y: cstring) {.noStackFrame.} =
|
||||
var i = 0
|
||||
while y[i] != '\0':
|
||||
add(x, y[i])
|
||||
@@ -1600,7 +1612,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
when defined(endb):
|
||||
proc endbStep()
|
||||
|
||||
# ----------------- IO Part --------------------------------------------------
|
||||
# ----------------- IO Part ------------------------------------------------
|
||||
|
||||
type
|
||||
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
|
||||
@@ -1783,7 +1795,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
while a[L] != nil: inc(L)
|
||||
result = cstringArrayToSeq(a, L)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
|
||||
## atomic increment of `memLoc`. Returns the value after the operation.
|
||||
@@ -1809,11 +1821,13 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
initStackBottom()
|
||||
initGC()
|
||||
|
||||
{.push stack_trace: off.}
|
||||
include "system/excpt"
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/arithm"
|
||||
{.pop.} # stack trace
|
||||
{.pop.} # stack trace
|
||||
|
||||
include "system/dyncalls"
|
||||
include "system/sets"
|
||||
@@ -1843,8 +1857,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
else:
|
||||
result = n.sons[n.len]
|
||||
|
||||
{.push stack_trace: off.}
|
||||
include "system/mmdisp"
|
||||
{.push stack_trace: off.}
|
||||
include "system/sysstr"
|
||||
{.pop.}
|
||||
|
||||
@@ -2109,3 +2123,7 @@ template doAssert*(cond: expr, msg = "") =
|
||||
{.line: InstantiationInfo().}:
|
||||
raiseAssert(astToStr(cond) & ' ' & msg)
|
||||
|
||||
|
||||
when defined(initDebugger):
|
||||
initDebugger()
|
||||
|
||||
|
||||
@@ -499,6 +499,7 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
|
||||
sysAssert(roundup(65, 8) == 72, "rawAlloc 1")
|
||||
sysAssert requestedSize >= sizeof(TFreeCell), "rawAlloc 2"
|
||||
var size = roundup(requestedSize, MemAlign)
|
||||
sysAssert(size >= requestedSize, "insufficient allocated size!")
|
||||
#c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
|
||||
if size <= SmallChunkSize-smallChunkOverhead():
|
||||
# allocate a small block: for small chunks, we use only its next pointer
|
||||
@@ -561,6 +562,7 @@ proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
|
||||
zeroMem(result, requestedSize)
|
||||
|
||||
proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
|
||||
sysAssert(allocInv(a), "rawDealloc: begin")
|
||||
var c = pageAddr(p)
|
||||
if isSmallChunk(c):
|
||||
|
||||
@@ -13,7 +13,7 @@ type
|
||||
TCell {.pure.} = object
|
||||
refcount: int # the refcount and some flags
|
||||
typ: PNimType
|
||||
when debugGC:
|
||||
when leakDetector:
|
||||
filename: cstring
|
||||
line: int
|
||||
|
||||
|
||||
@@ -8,15 +8,18 @@
|
||||
#
|
||||
|
||||
# This file implements the embedded debugger that can be linked
|
||||
# with the application. We should not use dynamic memory here as that
|
||||
# with the application. Mostly we do not use dynamic memory here as that
|
||||
# would interfere with the GC and trigger ON/OFF errors if the
|
||||
# user program corrupts memory. Unfortunately, for dispaying
|
||||
# variables we use the ``system.repr()`` proc which uses Nimrod
|
||||
# strings and thus allocates memory from the heap. Pity, but
|
||||
# I do not want to implement ``repr()`` twice. We also cannot deactivate
|
||||
# the GC here as that might run out of memory too quickly...
|
||||
# I do not want to implement ``repr()`` twice.
|
||||
|
||||
type
|
||||
TStaticStr {.pure, final.} = object
|
||||
len: int
|
||||
data: array[0..100, char]
|
||||
|
||||
TDbgState = enum
|
||||
dbOff, # debugger is turned off
|
||||
dbStepInto, # debugger is in tracing mode
|
||||
@@ -29,8 +32,8 @@ type
|
||||
low, high: int # range from low to high; if disabled
|
||||
# both low and high are set to their negative values
|
||||
# this makes the check faster and safes memory
|
||||
filename: string
|
||||
name: string # name of breakpoint
|
||||
filename: cstring
|
||||
name: TStaticStr # name of breakpoint
|
||||
|
||||
TVarSlot {.compilerproc, final.} = object # variable slots used for debugger:
|
||||
address: pointer
|
||||
@@ -47,12 +50,10 @@ type
|
||||
slots: array[0..10_000, TVarSlot]
|
||||
|
||||
var
|
||||
dbgInSignal: bool # wether the debugger is in the signal handler
|
||||
dbgIn: TFile # debugger input stream
|
||||
dbgUser: string = "s" # buffer for user input; first command is ``step_into``
|
||||
dbgUser: TStaticStr # buffer for user input; first command is ``step_into``
|
||||
# needs to be global cause we store the last command
|
||||
# in it
|
||||
dbgState: TDbgState = dbStepInto # state of debugger
|
||||
dbgState: TDbgState # state of debugger
|
||||
dbgBP: array[0..127, TDbgBreakpoint] # breakpoints
|
||||
dbgBPlen: int = 0
|
||||
|
||||
@@ -63,7 +64,36 @@ var
|
||||
|
||||
maxDisplayRecDepth: int = 5 # do not display too much data!
|
||||
|
||||
proc findBreakpoint(name: string): int =
|
||||
proc setLen(s: var TStaticStr, newLen=0) =
|
||||
s.len = newLen
|
||||
s.data[newLen] = '\0'
|
||||
|
||||
proc add(s: var TStaticStr, c: char) =
|
||||
if s.len < high(s.data)-1:
|
||||
s.data[s.len] = c
|
||||
s.data[s.len+1] = '\0'
|
||||
inc s.len
|
||||
|
||||
proc add(s: var TStaticStr, c: cstring) =
|
||||
var i = 0
|
||||
while c[i] != '\0':
|
||||
add s, c[i]
|
||||
inc i
|
||||
|
||||
proc assign(s: var TStaticStr, c: cstring) =
|
||||
setLen(s)
|
||||
add s, c
|
||||
|
||||
proc `==`(a, b: TStaticStr): bool =
|
||||
if a.len == b.len:
|
||||
for i in 0 .. a.len-1:
|
||||
if a.data[i] != b.data[i]: return false
|
||||
return true
|
||||
|
||||
proc `==`(a: TStaticStr, b: cstring): bool =
|
||||
result = c_strcmp(a.data, b) == 0
|
||||
|
||||
proc findBreakpoint(name: TStaticStr): int =
|
||||
# returns -1 if not found
|
||||
for i in countdown(dbgBPlen-1, 0):
|
||||
if name == dbgBP[i].name: return i
|
||||
@@ -72,16 +102,22 @@ proc findBreakpoint(name: string): int =
|
||||
proc ListBreakPoints() =
|
||||
write(stdout, "*** endb| Breakpoints:\n")
|
||||
for i in 0 .. dbgBPlen-1:
|
||||
write(stdout, dbgBP[i].name & ": " & $abs(dbgBP[i].low) & ".." &
|
||||
$abs(dbgBP[i].high) & dbgBP[i].filename)
|
||||
write(stdout, dbgBP[i].name.data)
|
||||
write(stdout, ": ")
|
||||
write(stdout, abs(dbgBP[i].low))
|
||||
write(stdout, "..")
|
||||
write(stdout, abs(dbgBP[i].high))
|
||||
write(stdout, dbgBP[i].filename)
|
||||
if dbgBP[i].low < 0:
|
||||
write(stdout, " [disabled]\n")
|
||||
else:
|
||||
write(stdout, "\n")
|
||||
write(stdout, "***\n")
|
||||
|
||||
proc openAppend(filename: string): TFile =
|
||||
if open(result, filename, fmAppend):
|
||||
proc openAppend(filename: cstring): TFile =
|
||||
var p: pointer = fopen(filename, "ab")
|
||||
if p != nil:
|
||||
result = cast[TFile](p)
|
||||
write(result, "----------------------------------------\n")
|
||||
|
||||
proc dbgRepr(p: pointer, typ: PNimType): string =
|
||||
@@ -101,13 +137,17 @@ proc writeVariable(stream: TFile, slot: TVarSlot) =
|
||||
writeln(stream, dbgRepr(slot.address, slot.typ))
|
||||
|
||||
proc ListFrame(stream: TFile, f: PExtendedFrame) =
|
||||
write(stream, "*** endb| Frame (" & $f.f.len & " slots):\n")
|
||||
write(stream, "*** endb| Frame (")
|
||||
write(stream, f.f.len)
|
||||
write(stream, " slots):\n")
|
||||
for i in 0 .. f.f.len-1:
|
||||
writeVariable(stream, f.slots[i])
|
||||
write(stream, "***\n")
|
||||
|
||||
proc ListVariables(stream: TFile, f: PExtendedFrame) =
|
||||
write(stream, "*** endb| Frame (" & $f.f.len & " slots):\n")
|
||||
write(stream, "*** endb| Frame (")
|
||||
write(stream, f.f.len)
|
||||
write(stream, " slots):\n")
|
||||
for i in 0 .. f.f.len-1:
|
||||
writeln(stream, f.slots[i].name)
|
||||
write(stream, "***\n")
|
||||
@@ -135,14 +175,19 @@ proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
|
||||
write(stdout, dbgFramePointer.procname)
|
||||
write(stdout, " ***\n")
|
||||
else:
|
||||
write(stdout, "*** endb| (procedure name not available) ***\n")
|
||||
write(stdout, "*** endb| (proc name not available) ***\n")
|
||||
|
||||
proc dbgShowExecutionPoint() =
|
||||
write(stdout, "*** endb| " & $framePtr.filename &
|
||||
"(" & $framePtr.line & ") " & $framePtr.procname & " ***\n")
|
||||
write(stdout, "*** endb| ")
|
||||
write(stdout, framePtr.filename)
|
||||
write(stdout, "(")
|
||||
write(stdout, framePtr.line)
|
||||
write(stdout, ") ")
|
||||
write(stdout, framePtr.procname)
|
||||
write(stdout, " ***\n")
|
||||
|
||||
when defined(windows) or defined(dos) or defined(os2):
|
||||
{.define: FileSystemCaseInsensitive.}
|
||||
const
|
||||
FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
|
||||
|
||||
proc fileMatches(c, bp: cstring): bool =
|
||||
# bp = breakpoint filename
|
||||
@@ -162,7 +207,7 @@ proc fileMatches(c, bp: cstring): bool =
|
||||
var x, y: char
|
||||
x = bp[i]
|
||||
y = c[i+clen-blen]
|
||||
when defined(FileSystemCaseInsensitive):
|
||||
when FileSystemCaseInsensitive:
|
||||
if x >= 'A' and x <= 'Z': x = chr(ord(x) - ord('A') + ord('a'))
|
||||
if y >= 'A' and y <= 'Z': y = chr(ord(y) - ord('A') + ord('a'))
|
||||
if x != y: return false
|
||||
@@ -175,7 +220,7 @@ proc dbgBreakpointReached(line: int): int =
|
||||
fileMatches(framePtr.filename, dbgBP[i].filename): return i
|
||||
return -1
|
||||
|
||||
proc scanAndAppendWord(src: string, a: var string, start: int): int =
|
||||
proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
|
||||
result = start
|
||||
# skip whitespace:
|
||||
while src[result] in {'\t', ' '}: inc(result)
|
||||
@@ -187,20 +232,20 @@ proc scanAndAppendWord(src: string, a: var string, start: int): int =
|
||||
else: break
|
||||
inc(result)
|
||||
|
||||
proc scanWord(src: string, a: var string, start: int): int =
|
||||
a = ""
|
||||
proc scanWord(src: cstring, a: var TStaticStr, start: int): int =
|
||||
setlen(a)
|
||||
result = scanAndAppendWord(src, a, start)
|
||||
|
||||
proc scanFilename(src: string, a: var string, start: int): int =
|
||||
proc scanFilename(src: cstring, a: var TStaticStr, start: int): int =
|
||||
result = start
|
||||
a = ""
|
||||
setLen a
|
||||
# skip whitespace:
|
||||
while src[result] in {'\t', ' '}: inc(result)
|
||||
while src[result] notin {'\t', ' ', '\0'}:
|
||||
add(a, src[result])
|
||||
inc(result)
|
||||
|
||||
proc scanNumber(src: string, a: var int, start: int): int =
|
||||
proc scanNumber(src: cstring, a: var int, start: int): int =
|
||||
result = start
|
||||
a = 0
|
||||
while src[result] in {'\t', ' '}: inc(result)
|
||||
@@ -222,7 +267,7 @@ q, quit quit the debugger and the program
|
||||
s, step single step, stepping into routine calls
|
||||
n, next single step, without stepping into routine calls
|
||||
f, skipcurrent continue execution until the current routine finishes
|
||||
c, continue continue execution until the next breakpoint
|
||||
c, continue, r, run continue execution until the next breakpoint
|
||||
i, ignore continue execution, ignore all breakpoints
|
||||
BREAKPOINTS
|
||||
b, break <name> [fromline [toline]] [file]
|
||||
@@ -247,14 +292,15 @@ maxdisplay <integer> set the display's recursion maximum
|
||||
proc InvalidCommand() =
|
||||
debugOut("[Warning] invalid command ignored (type 'h' for help) ")
|
||||
|
||||
proc hasExt(s: string): bool =
|
||||
proc hasExt(s: cstring): bool =
|
||||
# returns true if s has a filename extension
|
||||
for i in countdown(len(s)-1, 0):
|
||||
var i = 0
|
||||
while s[i] != '\0':
|
||||
if s[i] == '.': return true
|
||||
return false
|
||||
inc i
|
||||
|
||||
proc setBreakPoint(s: string, start: int) =
|
||||
var dbgTemp: string
|
||||
proc setBreakPoint(s: cstring, start: int) =
|
||||
var dbgTemp: TStaticStr
|
||||
var i = scanWord(s, dbgTemp, start)
|
||||
if i <= start:
|
||||
InvalidCommand()
|
||||
@@ -273,19 +319,22 @@ proc setBreakPoint(s: string, start: int) =
|
||||
if dbgBP[x].high == 0: # set to low:
|
||||
dbgBP[x].high = dbgBP[x].low
|
||||
i = scanFilename(s, dbgTemp, i)
|
||||
if not (dbgTemp.len == 0):
|
||||
if not hasExt(dbgTemp): add(dbgTemp, ".nim")
|
||||
dbgBP[x].filename = dbgTemp
|
||||
if dbgTemp.len != 0:
|
||||
debugOut("[Warning] explicit filename for breakpoint not supported")
|
||||
when false:
|
||||
if not hasExt(dbgTemp.data): add(dbgTemp, ".nim")
|
||||
dbgBP[x].filename = dbgTemp
|
||||
dbgBP[x].filename = framePtr.filename
|
||||
else: # use current filename
|
||||
dbgBP[x].filename = $framePtr.filename
|
||||
dbgBP[x].filename = framePtr.filename
|
||||
# skip whitespace:
|
||||
while s[i] in {' ', '\t'}: inc(i)
|
||||
if s[i] != '\0':
|
||||
dec(dbgBPLen) # remove buggy breakpoint
|
||||
InvalidCommand()
|
||||
|
||||
proc BreakpointSetEnabled(s: string, start, enabled: int) =
|
||||
var dbgTemp: string
|
||||
proc BreakpointSetEnabled(s: cstring, start, enabled: int) =
|
||||
var dbgTemp: TStaticStr
|
||||
var i = scanWord(s, dbgTemp, start)
|
||||
if i <= start:
|
||||
InvalidCommand()
|
||||
@@ -296,9 +345,9 @@ proc BreakpointSetEnabled(s: string, start, enabled: int) =
|
||||
dbgBP[x].low = -dbgBP[x].low
|
||||
dbgBP[x].high = -dbgBP[x].high
|
||||
|
||||
proc dbgEvaluate(stream: TFile, s: string, start: int,
|
||||
proc dbgEvaluate(stream: TFile, s: cstring, start: int,
|
||||
currFrame: PExtendedFrame) =
|
||||
var dbgTemp: string
|
||||
var dbgTemp: tstaticstr
|
||||
var i = scanWord(s, dbgTemp, start)
|
||||
while s[i] in {' ', '\t'}: inc(i)
|
||||
var f = currFrame
|
||||
@@ -311,92 +360,108 @@ proc dbgEvaluate(stream: TFile, s: string, start: int,
|
||||
if s[i] != '\0':
|
||||
debugOut("[Warning] could not parse expr ")
|
||||
return
|
||||
var j = findVariable(f, dbgTemp)
|
||||
var j = findVariable(f, dbgTemp.data)
|
||||
if j < 0:
|
||||
debugOut("[Warning] could not find variable ")
|
||||
return
|
||||
writeVariable(stream, f.slots[j])
|
||||
|
||||
proc dbgOut(s: string, start: int, currFrame: PExtendedFrame) =
|
||||
var dbgTemp: string
|
||||
proc dbgOut(s: cstring, start: int, currFrame: PExtendedFrame) =
|
||||
var dbgTemp: tstaticstr
|
||||
var i = scanFilename(s, dbgTemp, start)
|
||||
if dbgTemp.len == 0:
|
||||
InvalidCommand()
|
||||
return
|
||||
var stream = openAppend(dbgTemp)
|
||||
var stream = openAppend(dbgTemp.data)
|
||||
if stream == nil:
|
||||
debugOut("[Warning] could not open or create file ")
|
||||
return
|
||||
dbgEvaluate(stream, s, i, currFrame)
|
||||
close(stream)
|
||||
|
||||
proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) =
|
||||
var dbgTemp: string
|
||||
proc dbgStackFrame(s: cstring, start: int, currFrame: PExtendedFrame) =
|
||||
var dbgTemp: TStaticStr
|
||||
var i = scanFilename(s, dbgTemp, start)
|
||||
if dbgTemp.len == 0:
|
||||
# just write it to stdout:
|
||||
ListFrame(stdout, currFrame)
|
||||
else:
|
||||
var stream = openAppend(dbgTemp)
|
||||
var stream = openAppend(dbgTemp.data)
|
||||
if stream == nil:
|
||||
debugOut("[Warning] could not open or create file ")
|
||||
return
|
||||
ListFrame(stream, currFrame)
|
||||
close(stream)
|
||||
|
||||
proc readLine(f: TFile, line: var TStaticStr): bool =
|
||||
while True:
|
||||
var c = fgetc(f)
|
||||
if c < 0'i32:
|
||||
if line.len > 0: break
|
||||
else: return false
|
||||
if c == 10'i32: break # LF
|
||||
if c == 13'i32: # CR
|
||||
c = fgetc(f) # is the next char LF?
|
||||
if c != 10'i32: ungetc(c, f) # no, put the character back
|
||||
break
|
||||
add line, chr(int(c))
|
||||
result = true
|
||||
|
||||
proc dbgWriteStackTrace(f: PFrame)
|
||||
proc CommandPrompt() =
|
||||
# if we return from this routine, user code executes again
|
||||
var
|
||||
again = True
|
||||
dbgFramePtr = framePtr # for going down and up the stack
|
||||
dbgDown = 0 # how often we did go down
|
||||
dbgTemp: TStaticStr
|
||||
|
||||
while again:
|
||||
write(stdout, "*** endb| >>")
|
||||
var tmp = readLine(stdin)
|
||||
if tmp.len > 0: dbgUser = tmp
|
||||
let oldLen = dbgUser.len
|
||||
dbgUser.len = 0
|
||||
if not readLine(stdin, dbgUser): break
|
||||
if dbgUser.len == 0: dbgUser.len = oldLen
|
||||
# now look what we have to do:
|
||||
var dbgTemp: string
|
||||
var i = scanWord(dbgUser, dbgTemp, 0)
|
||||
case dbgTemp
|
||||
of "": InvalidCommand()
|
||||
of "s", "step":
|
||||
var i = scanWord(dbgUser.data, dbgTemp, 0)
|
||||
template `?`(x: expr): expr = dbgTemp == cstring(x)
|
||||
if ?"s" or ?"step":
|
||||
dbgState = dbStepInto
|
||||
again = false
|
||||
of "n", "next":
|
||||
elif ?"n" or ?"next":
|
||||
dbgState = dbStepOver
|
||||
dbgSkipToFrame = framePtr
|
||||
again = false
|
||||
of "f", "skipcurrent":
|
||||
elif ?"f" or ?"skipcurrent":
|
||||
dbgState = dbSkipCurrent
|
||||
dbgSkipToFrame = framePtr.prev
|
||||
again = false
|
||||
of "c", "continue":
|
||||
elif ?"c" or ?"continue" or ?"r" or ?"run":
|
||||
dbgState = dbBreakpoints
|
||||
again = false
|
||||
of "i", "ignore":
|
||||
elif ?"i" or ?"ignore":
|
||||
dbgState = dbOff
|
||||
again = false
|
||||
of "h", "help":
|
||||
elif ?"h" or ?"help":
|
||||
dbgHelp()
|
||||
of "q", "quit":
|
||||
elif ?"q" or ?"quit":
|
||||
dbgState = dbQuiting
|
||||
dbgAborting = True
|
||||
again = false
|
||||
quit(1) # BUGFIX: quit with error code > 0
|
||||
of "e", "eval":
|
||||
dbgEvaluate(stdout, dbgUser, i, cast[PExtendedFrame](dbgFramePtr))
|
||||
of "o", "out":
|
||||
dbgOut(dbgUser, i, cast[PExtendedFrame](dbgFramePtr))
|
||||
of "stackframe":
|
||||
dbgStackFrame(dbgUser, i, cast[PExtendedFrame](dbgFramePtr))
|
||||
of "w", "where":
|
||||
elif ?"e" or ?"eval":
|
||||
dbgEvaluate(stdout, dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
|
||||
elif ?"o" or ?"out":
|
||||
dbgOut(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
|
||||
elif ?"stackframe":
|
||||
dbgStackFrame(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
|
||||
elif ?"w" or ?"where":
|
||||
dbgShowExecutionPoint()
|
||||
of "l", "locals":
|
||||
elif ?"l" or ?"locals":
|
||||
ListVariables(stdout, cast[PExtendedFrame](dbgFramePtr))
|
||||
of "g", "globals":
|
||||
elif ?"g" or ?"globals":
|
||||
ListVariables(stdout, addr(dbgGlobalData))
|
||||
of "u", "up":
|
||||
elif ?"u" or ?"up":
|
||||
if dbgDown <= 0:
|
||||
debugOut("[Warning] cannot go up any further ")
|
||||
else:
|
||||
@@ -405,33 +470,32 @@ proc CommandPrompt() =
|
||||
dbgFramePtr = dbgFramePtr.prev
|
||||
dec(dbgDown)
|
||||
dbgShowCurrentProc(dbgFramePtr)
|
||||
of "d", "down":
|
||||
elif ?"d" or ?"down":
|
||||
if dbgFramePtr != nil:
|
||||
inc(dbgDown)
|
||||
dbgFramePtr = dbgFramePtr.prev
|
||||
dbgShowCurrentProc(dbgFramePtr)
|
||||
else:
|
||||
debugOut("[Warning] cannot go down any further ")
|
||||
of "bt", "backtrace":
|
||||
WriteStackTrace()
|
||||
of "b", "break":
|
||||
setBreakPoint(dbgUser, i)
|
||||
of "breakpoints":
|
||||
elif ?"bt" or ?"backtrace":
|
||||
dbgWriteStackTrace(framePtr)
|
||||
elif ?"b" or ?"break":
|
||||
setBreakPoint(dbgUser.data, i)
|
||||
elif ?"breakpoints":
|
||||
ListBreakPoints()
|
||||
of "disable":
|
||||
BreakpointSetEnabled(dbgUser, i, -1)
|
||||
of "enable":
|
||||
BreakpointSetEnabled(dbgUser, i, +1)
|
||||
of "maxdisplay":
|
||||
elif ?"disable":
|
||||
BreakpointSetEnabled(dbgUser.data, i, -1)
|
||||
elif ?"enable":
|
||||
BreakpointSetEnabled(dbgUser.data, i, +1)
|
||||
elif ?"maxdisplay":
|
||||
var parsed: int
|
||||
i = scanNumber(dbgUser, parsed, i)
|
||||
if dbgUser[i-1] in {'0'..'9'}:
|
||||
i = scanNumber(dbgUser.data, parsed, i)
|
||||
if dbgUser.data[i-1] in {'0'..'9'}:
|
||||
if parsed == 0: maxDisplayRecDepth = -1
|
||||
else: maxDisplayRecDepth = parsed
|
||||
else:
|
||||
InvalidCommand()
|
||||
else:
|
||||
InvalidCommand()
|
||||
else: InvalidCommand()
|
||||
|
||||
proc endbStep() =
|
||||
# we get into here if an unhandled exception has been raised
|
||||
@@ -441,10 +505,10 @@ proc endbStep() =
|
||||
CommandPrompt()
|
||||
|
||||
proc checkForBreakpoint() =
|
||||
var i = dbgBreakpointReached(framePtr.line)
|
||||
let i = dbgBreakpointReached(framePtr.line)
|
||||
if i >= 0:
|
||||
write(stdout, "*** endb| reached ")
|
||||
write(stdout, dbgBP[i].name)
|
||||
write(stdout, dbgBP[i].name.data)
|
||||
write(stdout, " in ")
|
||||
write(stdout, framePtr.filename)
|
||||
write(stdout, "(")
|
||||
@@ -458,16 +522,19 @@ proc checkForBreakpoint() =
|
||||
|
||||
proc dbgRegisterBreakpoint(line: int,
|
||||
filename, name: cstring) {.compilerproc.} =
|
||||
var x = dbgBPlen
|
||||
let x = dbgBPlen
|
||||
if x >= high(dbgBP):
|
||||
debugOut("[Warning] cannot register breakpoint")
|
||||
return
|
||||
inc(dbgBPlen)
|
||||
dbgBP[x].name = $name
|
||||
dbgBP[x].filename = $filename
|
||||
dbgBP[x].name.assign(name)
|
||||
dbgBP[x].filename = filename
|
||||
dbgBP[x].low = line
|
||||
dbgBP[x].high = line
|
||||
|
||||
proc dbgRegisterGlobal(name: cstring, address: pointer,
|
||||
typ: PNimType) {.compilerproc.} =
|
||||
var i = dbgGlobalData.f.len
|
||||
let i = dbgGlobalData.f.len
|
||||
if i >= high(dbgGlobalData.slots):
|
||||
debugOut("[Warning] cannot register global ")
|
||||
return
|
||||
@@ -476,14 +543,179 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
|
||||
dbgGlobalData.slots[i].address = address
|
||||
inc(dbgGlobalData.f.len)
|
||||
|
||||
type
|
||||
THash = int
|
||||
TWatchpoint {.pure, final.} = object
|
||||
name: cstring
|
||||
address: pointer
|
||||
typ: PNimType
|
||||
oldValue: THash
|
||||
|
||||
var
|
||||
Watchpoints: array [0..99, TWatchpoint]
|
||||
WatchpointsLen: int
|
||||
|
||||
proc `!&`(h: THash, val: int): THash {.inline.} =
|
||||
result = h +% val
|
||||
result = result +% result shl 10
|
||||
result = result xor (result shr 6)
|
||||
|
||||
proc `!$`(h: THash): THash {.inline.} =
|
||||
result = h +% h shl 3
|
||||
result = result xor (result shr 11)
|
||||
result = result +% result shl 15
|
||||
|
||||
proc hash(Data: Pointer, Size: int): THash =
|
||||
var h: THash = 0
|
||||
var p = cast[cstring](Data)
|
||||
var i = 0
|
||||
var s = size
|
||||
while s > 0:
|
||||
h = h !& ord(p[i])
|
||||
Inc(i)
|
||||
Dec(s)
|
||||
result = !$h
|
||||
|
||||
proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
|
||||
h: THash): THash
|
||||
proc genericHashAux(dest: Pointer, n: ptr TNimNode, shallow: bool,
|
||||
h: THash): THash =
|
||||
var d = cast[TAddress](dest)
|
||||
case n.kind
|
||||
of nkSlot:
|
||||
result = genericHashAux(cast[pointer](d +% n.offset), n.typ, shallow, h)
|
||||
of nkList:
|
||||
result = h
|
||||
for i in 0..n.len-1:
|
||||
result = result !& genericHashAux(dest, n.sons[i], shallow, result)
|
||||
of nkCase:
|
||||
result = h !& hash(cast[pointer](d +% n.offset), n.typ.size)
|
||||
var m = selectBranch(dest, n)
|
||||
if m != nil: result = genericHashAux(dest, m, shallow, result)
|
||||
of nkNone: sysAssert(false, "genericHashAux")
|
||||
|
||||
proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
|
||||
h: THash): THash =
|
||||
sysAssert(mt != nil, "genericHashAux 2")
|
||||
case mt.Kind
|
||||
of tyString:
|
||||
var x = cast[ppointer](dest)[]
|
||||
result = h
|
||||
if x != nil:
|
||||
let s = cast[NimString](x)
|
||||
let y = cast[pointer](cast[int](x) -% 2*sizeof(int))
|
||||
result = result !& hash(x, s.len + 2*sizeof(int))
|
||||
of tySequence:
|
||||
var x = cast[ppointer](dest)
|
||||
var dst = cast[taddress](cast[ppointer](dest)[])
|
||||
result = h
|
||||
if dst != 0:
|
||||
for i in 0..cast[pgenericseq](dst).len-1:
|
||||
result = result !& genericHashAux(
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
mt.Base, shallow, result)
|
||||
of tyObject, tyTuple:
|
||||
# we don't need to copy m_type field for tyObject, as they are equal anyway
|
||||
result = genericHashAux(dest, mt.node, shallow, h)
|
||||
of tyArray, tyArrayConstr:
|
||||
let d = cast[TAddress](dest)
|
||||
result = h
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size),
|
||||
mt.base, shallow, result)
|
||||
of tyRef:
|
||||
if shallow:
|
||||
result = h !& hash(dest, mt.size)
|
||||
else:
|
||||
var s = cast[ppointer](dest)[]
|
||||
if s != nil: result = genericHashAux(s, mt.base, shallow, h)
|
||||
else:
|
||||
result = h !& hash(dest, mt.size) # hash raw bits
|
||||
|
||||
proc genericHash(dest: Pointer, mt: PNimType): int =
|
||||
result = genericHashAux(dest, mt, false, 0)
|
||||
|
||||
proc dbgRegisterWatchpoint(address: pointer, name: cstring,
|
||||
typ: PNimType) {.compilerproc.} =
|
||||
let L = WatchpointsLen
|
||||
for i in 0.. <L:
|
||||
if Watchpoints[i].name == name:
|
||||
# address may have changed:
|
||||
Watchpoints[i].address = address
|
||||
return
|
||||
if L >= watchPoints.high:
|
||||
debugOut("[Warning] cannot register watchpoint")
|
||||
return
|
||||
Watchpoints[L].name = name
|
||||
Watchpoints[L].address = address
|
||||
Watchpoints[L].typ = typ
|
||||
Watchpoints[L].oldValue = genericHash(address, typ)
|
||||
inc WatchpointsLen
|
||||
|
||||
proc dbgWriteStackTrace(f: PFrame) =
|
||||
const
|
||||
firstCalls = 32
|
||||
var
|
||||
it = f
|
||||
i = 0
|
||||
total = 0
|
||||
tempFrames: array [0..127, PFrame]
|
||||
while it != nil and i <= high(tempFrames)-(firstCalls-1):
|
||||
# the (-1) is for a nil entry that marks where the '...' should occur
|
||||
tempFrames[i] = it
|
||||
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:
|
||||
tempFrames[i] = nil
|
||||
inc(i)
|
||||
while b != nil and i <= high(tempFrames):
|
||||
tempFrames[i] = b
|
||||
inc(i)
|
||||
b = b.prev
|
||||
for j in countdown(i-1, 0):
|
||||
if tempFrames[j] == nil:
|
||||
write(stdout, "(")
|
||||
write(stdout, (total-i-1))
|
||||
write(stdout, " calls omitted) ...")
|
||||
else:
|
||||
write(stdout, tempFrames[j].filename)
|
||||
if tempFrames[j].line > 0:
|
||||
write(stdout, '(')
|
||||
write(stdout, tempFrames[j].line)
|
||||
write(stdout, ')')
|
||||
write(stdout, ' ')
|
||||
write(stdout, tempFrames[j].procname)
|
||||
write(stdout, "\n")
|
||||
|
||||
proc checkWatchpoints =
|
||||
let L = WatchpointsLen
|
||||
for i in 0.. <L:
|
||||
let newHash = genericHash(Watchpoints[i].address, Watchpoints[i].typ)
|
||||
if newHash != Watchpoints[i].oldValue:
|
||||
dbgWriteStackTrace(framePtr)
|
||||
debugOut(Watchpoints[i].name)
|
||||
Watchpoints[i].oldValue = newHash
|
||||
|
||||
proc endb(line: int) {.compilerproc.} =
|
||||
# This proc is called before every Nimrod code line!
|
||||
# Thus, it must have as few parameters as possible to keep the
|
||||
# code size small!
|
||||
# Check if we are at an enabled breakpoint or "in the mood"
|
||||
if framePtr == nil: return
|
||||
let oldState = dbgState
|
||||
#dbgState = dbOff
|
||||
#if oldState != dbOff:
|
||||
checkWatchpoints()
|
||||
framePtr.line = line # this is done here for smaller code size!
|
||||
if dbgLineHook != nil: dbgLineHook()
|
||||
case dbgState
|
||||
case oldState
|
||||
of dbStepInto:
|
||||
# we really want the command prompt here:
|
||||
dbgShowExecutionPoint()
|
||||
@@ -497,3 +729,9 @@ proc endb(line: int) {.compilerproc.} =
|
||||
of dbBreakpoints: # debugger is only interested in breakpoints
|
||||
checkForBreakpoint()
|
||||
else: nil
|
||||
|
||||
proc initDebugger {.inline.} =
|
||||
dbgState = dbStepInto
|
||||
dbgUser.len = 1
|
||||
dbgUser.data[0] = 's'
|
||||
|
||||
|
||||
@@ -391,13 +391,6 @@ proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
||||
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
||||
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
||||
|
||||
proc internalAssert(file: cstring, line: int) {.noStackFrame, compilerproc.} =
|
||||
var
|
||||
e: ref EAssertionFailed
|
||||
new(e)
|
||||
asm """`e`.message = "[Assertion failure] file: "+`file`+", line: "+`line`"""
|
||||
raise e
|
||||
|
||||
include "system/hti"
|
||||
|
||||
proc isFatPointer(ti: PNimType): bool =
|
||||
|
||||
@@ -238,18 +238,6 @@ proc reraiseException() {.compilerRtl.} =
|
||||
else:
|
||||
raiseException(currException, currException.name)
|
||||
|
||||
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
||||
if not cond:
|
||||
var gAssertionFailed: ref EAssertionFailed
|
||||
new(gAssertionFailed)
|
||||
gAssertionFailed.msg = newStringOfCap(200)
|
||||
add(gAssertionFailed.msg, "[Assertion failure] file: ")
|
||||
add(gAssertionFailed.msg, file)
|
||||
add(gAssertionFailed.msg, " line: ")
|
||||
add(gAssertionFailed.msg, $line)
|
||||
add(gAssertionFailed.msg, "\n")
|
||||
raise gAssertionFailed
|
||||
|
||||
proc WriteStackTrace() =
|
||||
when hasSomeStackTrace:
|
||||
var s = ""
|
||||
|
||||
@@ -111,7 +111,7 @@ when debugGC:
|
||||
proc writeCell(msg: CString, c: PCell) =
|
||||
var kind = -1
|
||||
if c.typ != nil: kind = ord(c.typ.kind)
|
||||
when debugGC:
|
||||
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)
|
||||
else:
|
||||
@@ -277,6 +277,10 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
|
||||
# the test for '!= nil' is correct, but I got tired of the segfaults
|
||||
# resulting from the crappy stack checking:
|
||||
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
|
||||
else:
|
||||
# can't be an interior pointer if it's a stack location!
|
||||
sysAssert(interiorAllocatedPtr(gch.region, dest)==nil,
|
||||
"stack loc AND interior pointer")
|
||||
dest[] = src
|
||||
|
||||
proc initGC() =
|
||||
@@ -400,7 +404,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
|
||||
sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
# now it is buffered in the ZCT
|
||||
res.typ = typ
|
||||
when debugGC and not hasThreadSupport:
|
||||
when leakDetector and not hasThreadSupport:
|
||||
if framePtr != nil and framePtr.prev != nil:
|
||||
res.filename = framePtr.prev.filename
|
||||
res.line = framePtr.prev.line
|
||||
@@ -437,7 +441,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
# now it is buffered in the ZCT
|
||||
res.typ = typ
|
||||
when debugGC and not hasThreadSupport:
|
||||
when leakDetector and not hasThreadSupport:
|
||||
if framePtr != nil and framePtr.prev != nil:
|
||||
res.filename = framePtr.prev.filename
|
||||
res.line = framePtr.prev.line
|
||||
@@ -510,6 +514,9 @@ proc doOperation(p: pointer, op: TWalkOp) =
|
||||
sysAssert(c != nil, "doOperation: 1")
|
||||
case op # faster than function pointers because of easy prediction
|
||||
of waZctDecRef:
|
||||
#if not isAllocatedPtr(gch.region, c):
|
||||
# return
|
||||
# c_fprintf(c_stdout, "[GC] decref bug: %p", c)
|
||||
sysAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
|
||||
sysAssert(c.refcount >=% rcIncrement, "doOperation 2")
|
||||
c.refcount = c.refcount -% rcIncrement
|
||||
@@ -776,7 +783,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
|
||||
|
||||
proc collectCT(gch: var TGcHeap) =
|
||||
if (gch.zct.len >= ZctThreshold or (cycleGC and
|
||||
getOccupiedMem(gch.region) >= gch.cycleThreshold) or stressGC) and
|
||||
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
|
||||
gch.recGcLock == 0:
|
||||
sysAssert(allocInv(gch.region), "collectCT: begin")
|
||||
|
||||
@@ -789,7 +796,7 @@ proc collectCT(gch: var TGcHeap) =
|
||||
inc(gch.stat.stackScans)
|
||||
collectZCT(gch)
|
||||
when cycleGC:
|
||||
if getOccupiedMem(gch.region) >= gch.cycleThreshold or stressGC:
|
||||
if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
|
||||
collectCycles(gch)
|
||||
collectZCT(gch)
|
||||
inc(gch.stat.cycleCollections)
|
||||
|
||||
@@ -17,13 +17,16 @@ const
|
||||
debugGC = false # we wish to debug the GC...
|
||||
logGC = false
|
||||
traceGC = false # extensive debugging
|
||||
reallyDealloc = true # for debugging purposes this can be set to false
|
||||
alwaysCycleGC = false
|
||||
alwaysGC = false # collect after every memory allocation (for debugging)
|
||||
leakDetector = false
|
||||
overwriteFree = false
|
||||
|
||||
cycleGC = true # (de)activate the cycle GC
|
||||
stressGC = false
|
||||
reallyDealloc = true # for debugging purposes this can be set to false
|
||||
reallyOsDealloc = true
|
||||
coalescRight = true
|
||||
coalescLeft = true
|
||||
overwriteFree = false
|
||||
|
||||
type
|
||||
PPointer = ptr pointer
|
||||
@@ -229,7 +232,8 @@ else:
|
||||
include "system/alloc"
|
||||
|
||||
include "system/cellsets"
|
||||
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell")
|
||||
when not leakDetector:
|
||||
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell")
|
||||
include "system/gc"
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -125,7 +125,7 @@ proc readAll(file: TFile): TaintedString =
|
||||
# don't know the overall length of the TFile.
|
||||
var len = rawFileSize(file)
|
||||
if len >= 0:
|
||||
result = readAllFile(file, len).TaintedSTring
|
||||
result = readAllFile(file, len).TaintedString
|
||||
else:
|
||||
result = readAllBuffer(file).TaintedString
|
||||
|
||||
|
||||
@@ -25,40 +25,55 @@ proc bubbleSort[T](a: var openArray[T],
|
||||
when isMainModule:
|
||||
proc main() =
|
||||
const order = Ascending
|
||||
var data: seq[string]
|
||||
|
||||
for i in 0..10_000:
|
||||
var L = random(59)
|
||||
newSeq(data, L)
|
||||
var data: seq[string] = @[]
|
||||
|
||||
var L = random(59)
|
||||
for i in 0..1:
|
||||
echo "loop: ", i
|
||||
#newSeq(data, L)
|
||||
setLen(data, L)
|
||||
for j in 0 .. L-1:
|
||||
data[j] = $(math.random(90) - 10)
|
||||
assert getRefcount(data[j]) == 1
|
||||
{.watchpoint: data.}
|
||||
var copy = data
|
||||
for j in 0 .. L-1:
|
||||
assert getRefcount(copy[j]) == 1
|
||||
assert(cast[pointer](copy[j]) != cast[pointer](data[i]))
|
||||
|
||||
bubblesort(data, system.cmp, order)
|
||||
if not sorted(data, order):
|
||||
quit "bubblesort failed"
|
||||
sort(copy, cmp, order)
|
||||
if copy.len != data.len:
|
||||
quit "lengths differ!"
|
||||
for i in 0 .. copy.high:
|
||||
if copy[i] != data[i]:
|
||||
quit "algorithms differ!"
|
||||
|
||||
for i in 0..10_000:
|
||||
var data: seq[int]
|
||||
var L = random(59)
|
||||
newSeq(data, L)
|
||||
for j in 0 .. L-1:
|
||||
data[j] = (math.random(90) - 10)
|
||||
var copy = data
|
||||
sort(data, cmp[int], order)
|
||||
if not sorted(data, order):
|
||||
quit "sort for seq[int] failed"
|
||||
bubblesort(copy, system.cmp[int], order)
|
||||
sort(copy, cmp, order)
|
||||
for j in 0 .. L-1:
|
||||
let rc = getRefcount(data[j])
|
||||
if rc != 1:
|
||||
echo "RC IST ", rc, " j: ", j
|
||||
assert getRefcount(data[j]) == 1
|
||||
when false:
|
||||
if copy.len != data.len:
|
||||
quit "lengths differ!"
|
||||
for i in 0 .. copy.high:
|
||||
if copy[i] != data[i]:
|
||||
quit "algorithms differ!"
|
||||
when false:
|
||||
for i in 0..10_000:
|
||||
var data: seq[int]
|
||||
var L = random(59)
|
||||
newSeq(data, L)
|
||||
for j in 0 .. L-1:
|
||||
data[j] = (math.random(90) - 10)
|
||||
var copy = data
|
||||
sort(data, cmp[int], order)
|
||||
if not sorted(data, order):
|
||||
quit "sort for seq[int] failed"
|
||||
bubblesort(copy, system.cmp[int], order)
|
||||
if copy.len != data.len:
|
||||
quit "lengths differ!"
|
||||
for i in 0 .. copy.high:
|
||||
if copy[i] != data[i]:
|
||||
quit "algorithms differ!"
|
||||
|
||||
main()
|
||||
|
||||
|
||||
2
todo.txt
2
todo.txt
@@ -2,6 +2,8 @@ version 0.8.14
|
||||
==============
|
||||
|
||||
- bug: tsortdev does not run with native GC
|
||||
- object {.pure, final.} does not work again!
|
||||
|
||||
|
||||
version 0.9.0
|
||||
=============
|
||||
|
||||
@@ -119,6 +119,11 @@ Compiler Additions
|
||||
for specifying modules that will be automatically imported/incluced.
|
||||
- ``nimrod i`` can now optionally be given a module to execute.
|
||||
- The compiler now performs a simple alias analysis to generate better code.
|
||||
- The compiler and ENDB now support *watchpoints*.
|
||||
- The compiler now supports proper compile time expressions of type ``bool``
|
||||
for ``on|off`` switches in pragmas. In order to not break existing code,
|
||||
``on`` and ``off`` are now aliases for ``true`` and ``false`` and declared
|
||||
in the system module.
|
||||
|
||||
|
||||
Library Additions
|
||||
|
||||
Reference in New Issue
Block a user