pragma on/off improvements; endb rewritten

This commit is contained in:
Araq 2012-01-28 23:22:44 +01:00
commit 8d19a93f1a
23 changed files with 517 additions and 202 deletions

View file

@ -662,7 +662,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
[rdCharLoc(b), first, intLiteral(lastOrd(ty))]) [rdCharLoc(b), first, intLiteral(lastOrd(ty))])
if d.k == locNone: d.s = a.s if d.k == locNone: d.s = a.s
putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), 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) = proc genCStringElem(p: BProc, e: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
@ -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",

View file

@ -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 =

View file

@ -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,13 +154,11 @@ 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) LocalError(n.info, errOnOrOffExpected)
else:
LocalError(n.info, errOnOrOffExpected)
proc onOff(c: PContext, n: PNode, op: TOptions) = proc onOff(c: PContext, n: PNode, op: TOptions) =
if IsTurnedOn(c, n): gOptions = gOptions + op if IsTurnedOn(c, n): gOptions = gOptions + op
@ -211,28 +208,29 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
else: else:
incl(sym.loc.flags, lfExportLib) 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 if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and
(n.sons[0].kind == nkBracketExpr) and (n.sons[0].kind == nkBracketExpr) and
(n.sons[0].sons[1].kind == nkIdent) 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 var nk: TNoteKind
case whichKeyword(n.sons[0].sons[0].ident) case whichKeyword(n.sons[0].sons[0].ident)
of wHint: of wHint:
var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s) var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(hintMin)) if x >= 0: nk = TNoteKind(x + ord(hintMin))
else: invalidPragma(n) else: invalidPragma(n)
of wWarning: of wWarning:
var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s) var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(warnMin)) if x >= 0: nk = TNoteKind(x + ord(warnMin))
else: InvalidPragma(n) else: InvalidPragma(n)
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)
@ -335,8 +333,8 @@ proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
if found == "": found = f # use the default if found == "": found = f # use the default
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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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"]

View file

@ -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
===================== =====================

View file

@ -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))

View file

@ -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()

View file

@ -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):

View file

@ -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

View file

@ -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")
dbgBP[x].filename = dbgTemp when false:
if not hasExt(dbgTemp.data): add(dbgTemp, ".nim")
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'

View file

@ -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 =

View file

@ -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 = ""

View file

@ -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)

View file

@ -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,7 +232,8 @@ else:
include "system/alloc" include "system/alloc"
include "system/cellsets" include "system/cellsets"
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell") when not leakDetector:
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell")
include "system/gc" include "system/gc"
{.pop.} {.pop.}

View file

@ -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

View file

@ -25,40 +25,55 @@ 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) for i in 0..1:
newSeq(data, L) 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)
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: sort(copy, cmp, order)
var data: seq[int] for j in 0 .. L-1:
var L = random(59) let rc = getRefcount(data[j])
newSeq(data, L) if rc != 1:
for j in 0 .. L-1: echo "RC IST ", rc, " j: ", j
data[j] = (math.random(90) - 10) assert getRefcount(data[j]) == 1
var copy = data when false:
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: 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:
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() main()

View file

@ -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
============= =============

View file

@ -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