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