Remove ENDB (#12095)
This commit is contained in:
parent
82d5e773e3
commit
f9600b7207
16 changed files with 19 additions and 1216 deletions
|
|
@ -1161,36 +1161,10 @@ proc genEmit(p: BProc, t: PNode) =
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
line(p, cpsStmts, s)
|
line(p, cpsStmts, s)
|
||||||
|
|
||||||
proc genBreakPoint(p: BProc, t: PNode) =
|
|
||||||
var name: string
|
|
||||||
if optEndb in p.options:
|
|
||||||
if t.kind == nkExprColonExpr:
|
|
||||||
assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit})
|
|
||||||
name = normalize(t.sons[1].strVal)
|
|
||||||
else:
|
|
||||||
inc(p.module.g.breakPointId)
|
|
||||||
name = "bp" & $p.module.g.breakPointId
|
|
||||||
genLineDir(p, t) # BUGFIX
|
|
||||||
appcg(p.module, p.module.g.breakpoints,
|
|
||||||
"#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [
|
|
||||||
toLinenumber(t.info), makeCString(toFilename(p.config, t.info)),
|
|
||||||
makeCString(name)])
|
|
||||||
|
|
||||||
proc genWatchpoint(p: BProc, n: PNode) =
|
|
||||||
if optEndb notin p.options: return
|
|
||||||
var a: TLoc
|
|
||||||
initLocExpr(p, n.sons[1], a)
|
|
||||||
let typ = skipTypes(n.sons[1].typ, abstractVarRange)
|
|
||||||
lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n",
|
|
||||||
[addrLoc(p.config, a), makeCString(renderTree(n.sons[1])),
|
|
||||||
genTypeInfo(p.module, typ, n.info)])
|
|
||||||
|
|
||||||
proc genPragma(p: BProc, n: PNode) =
|
proc genPragma(p: BProc, n: PNode) =
|
||||||
for it in n.sons:
|
for it in n.sons:
|
||||||
case whichPragma(it)
|
case whichPragma(it)
|
||||||
of wEmit: genEmit(p, it)
|
of wEmit: genEmit(p, it)
|
||||||
of wBreakpoint: genBreakPoint(p, it)
|
|
||||||
of wWatchPoint: genWatchpoint(p, it)
|
|
||||||
of wInjectStmt:
|
of wInjectStmt:
|
||||||
var p = newProc(nil, p.module)
|
var p = newProc(nil, p.module)
|
||||||
p.options = p.options - {optLineTrace, optStackTrace}
|
p.options = p.options - {optLineTrace, optStackTrace}
|
||||||
|
|
|
||||||
|
|
@ -264,13 +264,7 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||||
if optEmbedOrigSrc in p.config.globalOptions:
|
if optEmbedOrigSrc in p.config.globalOptions:
|
||||||
add(p.s(cpsStmts), ~"//" & sourceLine(p.config, t.info) & "\L")
|
add(p.s(cpsStmts), ~"//" & sourceLine(p.config, t.info) & "\L")
|
||||||
genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
|
genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
|
||||||
if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
|
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
|
||||||
(p.prc == nil or sfPure notin p.prc.flags):
|
|
||||||
if freshLineInfo(p, t.info):
|
|
||||||
linefmt(p, cpsStmts, "#endb($1, $2);$N",
|
|
||||||
[line, makeCString(toFilename(p.config, t.info))])
|
|
||||||
elif ({optLineTrace, optStackTrace} * p.options ==
|
|
||||||
{optLineTrace, optStackTrace}) and
|
|
||||||
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
||||||
if freshLineInfo(p, t.info):
|
if freshLineInfo(p, t.info):
|
||||||
linefmt(p, cpsStmts, "nimln_($1, $2);$n",
|
linefmt(p, cpsStmts, "nimln_($1, $2);$n",
|
||||||
|
|
@ -479,19 +473,6 @@ proc deinitGCFrame(p: BProc): Rope =
|
||||||
result = ropecg(p.module,
|
result = ropecg(p.module,
|
||||||
"if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n", [])
|
"if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n", [])
|
||||||
|
|
||||||
proc localDebugInfo(p: BProc, s: PSym, retType: PType) =
|
|
||||||
if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
|
|
||||||
# XXX work around a bug: No type information for open arrays possible:
|
|
||||||
if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
|
|
||||||
var a = "&" & s.loc.r
|
|
||||||
if s.kind == skParam and ccgIntroducedPtr(p.config, s, retType): a = s.loc.r
|
|
||||||
lineF(p, cpsInit,
|
|
||||||
"FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
|
|
||||||
[p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
|
|
||||||
genTypeInfo(p.module, s.loc.t, s.info)])
|
|
||||||
inc(p.maxFrameLen)
|
|
||||||
inc p.blocks[p.blocks.len-1].frameLen
|
|
||||||
|
|
||||||
proc localVarDecl(p: BProc; n: PNode): Rope =
|
proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if s.loc.k == locNone:
|
if s.loc.k == locNone:
|
||||||
|
|
@ -515,7 +496,6 @@ proc assignLocalVar(p: BProc, n: PNode) =
|
||||||
let nl = if optLineDir in p.config.options: "" else: "\L"
|
let nl = if optLineDir in p.config.options: "" else: "\L"
|
||||||
let decl = localVarDecl(p, n) & ";" & nl
|
let decl = localVarDecl(p, n) & ";" & nl
|
||||||
line(p, cpsLocals, decl)
|
line(p, cpsLocals, decl)
|
||||||
localDebugInfo(p, n.sym, nil)
|
|
||||||
|
|
||||||
include ccgthreadvars
|
include ccgthreadvars
|
||||||
|
|
||||||
|
|
@ -562,17 +542,10 @@ proc assignGlobalVar(p: BProc, n: PNode) =
|
||||||
if p.withinLoop > 0:
|
if p.withinLoop > 0:
|
||||||
# fixes tests/run/tzeroarray:
|
# fixes tests/run/tzeroarray:
|
||||||
resetLoc(p, s.loc)
|
resetLoc(p, s.loc)
|
||||||
if p.module.module.options * {optStackTrace, optEndb} ==
|
|
||||||
{optStackTrace, optEndb}:
|
|
||||||
appcg(p.module, p.module.s[cfsDebugInit],
|
|
||||||
"#dbgRegisterGlobal($1, &$2, $3);$n",
|
|
||||||
[makeCString(normalize(s.owner.name.s & '.' & s.name.s)),
|
|
||||||
s.loc.r, genTypeInfo(p.module, s.typ, n.info)])
|
|
||||||
|
|
||||||
proc assignParam(p: BProc, s: PSym, retType: PType) =
|
proc assignParam(p: BProc, s: PSym, retType: PType) =
|
||||||
assert(s.loc.r != nil)
|
assert(s.loc.r != nil)
|
||||||
scopeMangledParam(p, s)
|
scopeMangledParam(p, s)
|
||||||
localDebugInfo(p, s, retType)
|
|
||||||
|
|
||||||
proc fillProcLoc(m: BModule; n: PNode) =
|
proc fillProcLoc(m: BModule; n: PNode) =
|
||||||
let sym = n.sym
|
let sym = n.sym
|
||||||
|
|
@ -689,7 +662,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
|
||||||
[loadlib, genStringLiteral(m, lib.path)])
|
[loadlib, genStringLiteral(m, lib.path)])
|
||||||
else:
|
else:
|
||||||
var p = newProc(nil, m)
|
var p = newProc(nil, m)
|
||||||
p.options = p.options - {optStackTrace, optEndb}
|
p.options = p.options - {optStackTrace}
|
||||||
var dest: TLoc
|
var dest: TLoc
|
||||||
initLoc(dest, locTemp, lib.path, OnStack)
|
initLoc(dest, locTemp, lib.path, OnStack)
|
||||||
dest.r = getTempName(m)
|
dest.r = getTempName(m)
|
||||||
|
|
@ -1318,7 +1291,6 @@ proc genMainProc(m: BModule) =
|
||||||
PreMainBody = "$N" &
|
PreMainBody = "$N" &
|
||||||
"void PreMainInner(void) {$N" &
|
"void PreMainInner(void) {$N" &
|
||||||
"$2" &
|
"$2" &
|
||||||
"$3" &
|
|
||||||
"}$N$N" &
|
"}$N$N" &
|
||||||
PosixCmdLine &
|
PosixCmdLine &
|
||||||
"void PreMain(void) {$N" &
|
"void PreMain(void) {$N" &
|
||||||
|
|
@ -1408,17 +1380,11 @@ proc genMainProc(m: BModule) =
|
||||||
elif m.config.target.targetOS == osGenode:
|
elif m.config.target.targetOS == osGenode:
|
||||||
m.includeHeader("<libc/component.h>")
|
m.includeHeader("<libc/component.h>")
|
||||||
|
|
||||||
if optEndb in m.config.options:
|
|
||||||
for i in 0..<m.config.m.fileInfos.len:
|
|
||||||
m.g.breakpoints.addf("dbgRegisterFilename($1);$N",
|
|
||||||
[m.config.m.fileInfos[i].projPath.string.makeCString])
|
|
||||||
|
|
||||||
let initStackBottomCall =
|
let initStackBottomCall =
|
||||||
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
|
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
|
||||||
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
|
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
|
||||||
inc(m.labels)
|
inc(m.labels)
|
||||||
appcg(m, m.s[cfsProcs], PreMainBody, [
|
appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit])
|
||||||
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit])
|
|
||||||
|
|
||||||
if m.config.target.targetOS == osWindows and
|
if m.config.target.targetOS == osWindows and
|
||||||
m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
|
m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
|
||||||
|
|
@ -2024,11 +1990,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||||
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
||||||
discard cgsym(m, "initThreadVarsEmulation")
|
discard cgsym(m, "initThreadVarsEmulation")
|
||||||
|
|
||||||
if m.g.breakpoints != nil:
|
|
||||||
discard cgsym(m, "dbgRegisterBreakpoint")
|
|
||||||
if optEndb in m.config.options:
|
|
||||||
discard cgsym(m, "dbgRegisterFilename")
|
|
||||||
|
|
||||||
if m.g.forwardedProcs.len == 0:
|
if m.g.forwardedProcs.len == 0:
|
||||||
incl m.flags, objHasKidsValid
|
incl m.flags, objHasKidsValid
|
||||||
let disp = generateMethodDispatchers(graph)
|
let disp = generateMethodDispatchers(graph)
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,6 @@ type
|
||||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||||
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
||||||
generatedHeader*: BModule
|
generatedHeader*: BModule
|
||||||
breakPointId*: int
|
|
||||||
breakpoints*: Rope # later the breakpoints are inserted into the main proc
|
|
||||||
typeInfoMarker*: TypeCacheWithOwner
|
typeInfoMarker*: TypeCacheWithOwner
|
||||||
config*: ConfigRef
|
config*: ConfigRef
|
||||||
graph*: ModuleGraph
|
graph*: ModuleGraph
|
||||||
|
|
|
||||||
|
|
@ -266,7 +266,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||||
of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
|
of "threadanalysis": result = contains(conf.globalOptions, optThreadAnalysis)
|
||||||
of "stacktrace": result = contains(conf.options, optStackTrace)
|
of "stacktrace": result = contains(conf.options, optStackTrace)
|
||||||
of "linetrace": result = contains(conf.options, optLineTrace)
|
of "linetrace": result = contains(conf.options, optLineTrace)
|
||||||
of "debugger": result = contains(conf.options, optEndb)
|
of "debugger": result = contains(conf.globalOptions, optCDebug)
|
||||||
of "profiler": result = contains(conf.options, optProfiler)
|
of "profiler": result = contains(conf.options, optProfiler)
|
||||||
of "memtracker": result = contains(conf.options, optMemTracker)
|
of "memtracker": result = contains(conf.options, optMemTracker)
|
||||||
of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions
|
of "checks", "x": result = conf.options * ChecksOptions == ChecksOptions
|
||||||
|
|
@ -473,24 +473,18 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||||
of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
|
of "linetrace": processOnOffSwitch(conf, {optLineTrace}, arg, pass, info)
|
||||||
of "debugger":
|
of "debugger":
|
||||||
case arg.normalize
|
case arg.normalize
|
||||||
of "on", "endb":
|
of "on", "native", "gdb":
|
||||||
conf.options.incl optEndb
|
conf.globalOptions.incl optCDebug
|
||||||
defineSymbol(conf.symbols, "endb")
|
conf.options.incl optLineDir
|
||||||
|
#defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
|
||||||
of "off":
|
of "off":
|
||||||
conf.options.excl optEndb
|
conf.globalOptions.excl optCDebug
|
||||||
undefSymbol(conf.symbols, "endb")
|
|
||||||
of "native", "gdb":
|
|
||||||
incl(conf.globalOptions, optCDebug)
|
|
||||||
conf.options = conf.options + {optLineDir} - {optEndb}
|
|
||||||
#defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
|
|
||||||
undefSymbol(conf.symbols, "endb")
|
|
||||||
else:
|
else:
|
||||||
localError(conf, info, "expected endb|gdb but found " & arg)
|
localError(conf, info, "expected native|gdb|on|off but found " & arg)
|
||||||
of "g": # alias for --debugger:native
|
of "g": # alias for --debugger:native
|
||||||
incl(conf.globalOptions, optCDebug)
|
conf.globalOptions.incl optCDebug
|
||||||
conf.options = conf.options + {optLineDir} - {optEndb}
|
conf.options.incl optLineDir
|
||||||
#defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
|
#defineSymbol(conf.symbols, "nimTypeNames") # type names are used in gdb pretty printing
|
||||||
undefSymbol(conf.symbols, "endb")
|
|
||||||
of "profiler":
|
of "profiler":
|
||||||
processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
|
processOnOffSwitch(conf, {optProfiler}, arg, pass, info)
|
||||||
if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler")
|
if optProfiler in conf.options: defineSymbol(conf.symbols, "profiler")
|
||||||
|
|
|
||||||
|
|
@ -658,11 +658,7 @@ proc genLineDir(p: PProc, n: PNode) =
|
||||||
if optLineDir in p.options:
|
if optLineDir in p.options:
|
||||||
lineF(p, "// line $2 \"$1\"$n",
|
lineF(p, "// line $2 \"$1\"$n",
|
||||||
[rope(toFilename(p.config, n.info)), rope(line)])
|
[rope(toFilename(p.config, n.info)), rope(line)])
|
||||||
if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
|
if hasFrameInfo(p):
|
||||||
((p.prc == nil) or sfPure notin p.prc.flags):
|
|
||||||
useMagic(p, "endb")
|
|
||||||
lineF(p, "endb($1);$n", [rope(line)])
|
|
||||||
elif hasFrameInfo(p):
|
|
||||||
lineF(p, "F.line = $1;$n", [rope(line)])
|
lineF(p, "F.line = $1;$n", [rope(line)])
|
||||||
|
|
||||||
proc genWhileStmt(p: PProc, n: PNode) =
|
proc genWhileStmt(p: PProc, n: PNode) =
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ type # please make sure we have under 32 options
|
||||||
optAssert, optLineDir, optWarns, optHints,
|
optAssert, optLineDir, optWarns, optHints,
|
||||||
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
|
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
|
||||||
optLineTrace, # line tracing support (includes stack tracing)
|
optLineTrace, # line tracing support (includes stack tracing)
|
||||||
optEndb, # embedded debugger
|
|
||||||
optByRef, # use pass by ref for objects
|
optByRef, # use pass by ref for objects
|
||||||
# (for interfacing with C)
|
# (for interfacing with C)
|
||||||
optProfiler, # profiler turned on
|
optProfiler, # profiler turned on
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ const
|
||||||
wWarnings, wHints,
|
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, wWatchPoint, wPassl, wPassc,
|
wPassl, wPassc,
|
||||||
wDeadCodeElimUnused, # deprecated, always on
|
wDeadCodeElimUnused, # deprecated, always on
|
||||||
wDeprecated,
|
wDeprecated,
|
||||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||||
|
|
@ -345,7 +345,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
|
||||||
of wLineDir: {optLineDir}
|
of wLineDir: {optLineDir}
|
||||||
of wStackTrace: {optStackTrace}
|
of wStackTrace: {optStackTrace}
|
||||||
of wLineTrace: {optLineTrace}
|
of wLineTrace: {optLineTrace}
|
||||||
of wDebugger: {optEndb}
|
of wDebugger: {optNone}
|
||||||
of wProfiler: {optProfiler, optMemTracker}
|
of wProfiler: {optProfiler, optMemTracker}
|
||||||
of wMemTracker: {optMemTracker}
|
of wMemTracker: {optMemTracker}
|
||||||
of wByRef: {optByRef}
|
of wByRef: {optByRef}
|
||||||
|
|
@ -513,15 +513,6 @@ proc processLink(c: PContext, n: PNode) =
|
||||||
extccomp.addExternalFileToLink(c.config, found)
|
extccomp.addExternalFileToLink(c.config, found)
|
||||||
recordPragma(c, n, "link", found.string)
|
recordPragma(c, n, "link", found.string)
|
||||||
|
|
||||||
proc pragmaBreakpoint(c: PContext, n: PNode) =
|
|
||||||
discard getOptionalStr(c, n, "")
|
|
||||||
|
|
||||||
proc pragmaWatchpoint(c: PContext, n: PNode) =
|
|
||||||
if n.kind in nkPragmaCallKinds and n.len == 2:
|
|
||||||
n.sons[1] = c.semExpr(c, n.sons[1])
|
|
||||||
else:
|
|
||||||
invalidPragma(c, 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:
|
||||||
|
|
@ -996,8 +987,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
let s = expectStrLit(c, it)
|
let s = expectStrLit(c, it)
|
||||||
extccomp.addCompileOption(c.config, s)
|
extccomp.addCompileOption(c.config, s)
|
||||||
recordPragma(c, it, "passc", s)
|
recordPragma(c, it, "passc", s)
|
||||||
of wBreakpoint: pragmaBreakpoint(c, it)
|
|
||||||
of wWatchPoint: pragmaWatchpoint(c, it)
|
|
||||||
of wPush:
|
of wPush:
|
||||||
processPush(c, n, i + 1)
|
processPush(c, n, i + 1)
|
||||||
result = true
|
result = true
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ type
|
||||||
wCompilerProc, wCore, wProcVar, wBase, wUsed,
|
wCompilerProc, wCore, wProcVar, wBase, wUsed,
|
||||||
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
||||||
wLineDir, wStackTrace, wLineTrace, wLink, wCompile,
|
wLineDir, wStackTrace, wLineTrace, wLink, wCompile,
|
||||||
wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
|
wLinksys, wDeprecated, wVarargs, wCallconv, wDebugger,
|
||||||
wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
|
wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
|
||||||
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks,
|
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks,
|
||||||
wBoundChecks, wOverflowChecks, wNilChecks,
|
wBoundChecks, wOverflowChecks, wNilChecks,
|
||||||
|
|
@ -62,8 +62,7 @@ type
|
||||||
wCompileTime, wNoInit,
|
wCompileTime, wNoInit,
|
||||||
wPassc, wPassl, wBorrow, wDiscardable,
|
wPassc, wPassl, wBorrow, wDiscardable,
|
||||||
wFieldChecks,
|
wFieldChecks,
|
||||||
wWatchPoint, wSubsChar,
|
wSubsChar, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto,
|
||||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto,
|
|
||||||
wInjectStmt, wExperimental,
|
wInjectStmt, wExperimental,
|
||||||
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
||||||
wAsmNoStackFrame,
|
wAsmNoStackFrame,
|
||||||
|
|
@ -138,7 +137,7 @@ const
|
||||||
"fatal", "error", "warning", "hint", "line",
|
"fatal", "error", "warning", "hint", "line",
|
||||||
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
|
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
|
||||||
"link", "compile", "linksys", "deprecated", "varargs",
|
"link", "compile", "linksys", "deprecated", "varargs",
|
||||||
"callconv", "breakpoint", "debugger", "nimcall", "stdcall",
|
"callconv", "debugger", "nimcall", "stdcall",
|
||||||
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
|
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
|
||||||
"noconv", "on", "off", "checks", "rangechecks", "boundchecks",
|
"noconv", "on", "off", "checks", "rangechecks", "boundchecks",
|
||||||
"overflowchecks", "nilchecks",
|
"overflowchecks", "nilchecks",
|
||||||
|
|
@ -152,7 +151,6 @@ const
|
||||||
"pragma",
|
"pragma",
|
||||||
"compiletime", "noinit",
|
"compiletime", "noinit",
|
||||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||||
"watchpoint",
|
|
||||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||||
"computedgoto", "injectstmt", "experimental",
|
"computedgoto", "injectstmt", "experimental",
|
||||||
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
||||||
|
|
|
||||||
203
doc/endb.rst
203
doc/endb.rst
|
|
@ -1,203 +0,0 @@
|
||||||
==============================================
|
|
||||||
Embedded Nim Debugger (ENDB) User Guide
|
|
||||||
==============================================
|
|
||||||
|
|
||||||
:Author: Andreas Rumpf
|
|
||||||
:Version: |nimversion|
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
|
|
||||||
**WARNING**: ENDB is not maintained anymore! Please help if you're interested
|
|
||||||
in this tool.
|
|
||||||
|
|
||||||
Nim comes with a platform independent debugger -
|
|
||||||
the Embedded Nim Debugger (ENDB). The debugger is
|
|
||||||
*embedded* into your executable if it has been
|
|
||||||
compiled with the ``--debugger:on`` command line option.
|
|
||||||
This also defines the conditional symbol ``ENDB`` for you.
|
|
||||||
|
|
||||||
Note: You must not compile your program with the ``--app:gui``
|
|
||||||
command line option because then there would be no console
|
|
||||||
available for the debugger.
|
|
||||||
|
|
||||||
If you start your program the debugger will immediately show
|
|
||||||
a prompt on the console. You can now enter a command. The next sections
|
|
||||||
deal with the possible commands. As usual in Nim in all commands
|
|
||||||
underscores and case do not matter. Optional components of a command
|
|
||||||
are listed in brackets ``[...]`` here.
|
|
||||||
|
|
||||||
|
|
||||||
General Commands
|
|
||||||
================
|
|
||||||
|
|
||||||
``h``, ``help``
|
|
||||||
Display a quick reference of the possible commands.
|
|
||||||
|
|
||||||
``q``, ``quit``
|
|
||||||
Quit the debugger and the program.
|
|
||||||
|
|
||||||
<ENTER>
|
|
||||||
(Without any typed command) repeat the previous debugger command.
|
|
||||||
If there is no previous command, ``step_into`` is assumed.
|
|
||||||
|
|
||||||
Executing Commands
|
|
||||||
==================
|
|
||||||
|
|
||||||
``s``, ``step_into``
|
|
||||||
Single step, stepping into routine calls.
|
|
||||||
|
|
||||||
``n``, ``step_over``
|
|
||||||
Single step, without stepping into routine calls.
|
|
||||||
|
|
||||||
``f``, ``skip_current``
|
|
||||||
Continue execution until the current routine finishes.
|
|
||||||
|
|
||||||
``c``, ``continue``
|
|
||||||
Continue execution until the next breakpoint.
|
|
||||||
|
|
||||||
``i``, ``ignore``
|
|
||||||
Continue execution, ignore all breakpoints. This effectively quits
|
|
||||||
the debugger and runs the program until it finishes.
|
|
||||||
|
|
||||||
|
|
||||||
Breakpoint Commands
|
|
||||||
===================
|
|
||||||
|
|
||||||
``b``, ``setbreak`` [fromline [toline]] [file]
|
|
||||||
Set a new breakpoint for the given file
|
|
||||||
and line numbers. If no file is given, the current execution point's
|
|
||||||
filename is used. If the filename has no extension, ``.nim`` is
|
|
||||||
appended for your convenience.
|
|
||||||
If no line numbers are given, the current execution point's
|
|
||||||
line is used. If both ``fromline`` and ``toline`` are given the
|
|
||||||
breakpoint contains a line number range. Some examples if it is still
|
|
||||||
unclear:
|
|
||||||
|
|
||||||
* ``b 12 15 thallo`` creates a breakpoint that
|
|
||||||
will be triggered if the instruction pointer reaches one of the
|
|
||||||
lines 12-15 in the file ``thallo.nim``.
|
|
||||||
* ``b 12 thallo`` creates a breakpoint that
|
|
||||||
will be triggered if the instruction pointer reaches the
|
|
||||||
line 12 in the file ``thallo.nim``.
|
|
||||||
* ``b 12`` creates a breakpoint that
|
|
||||||
will be triggered if the instruction pointer reaches the
|
|
||||||
line 12 in the current file.
|
|
||||||
* ``b`` creates a breakpoint that
|
|
||||||
will be triggered if the instruction pointer reaches the
|
|
||||||
current line in the current file again.
|
|
||||||
|
|
||||||
``breakpoints``
|
|
||||||
Display the entire breakpoint list.
|
|
||||||
|
|
||||||
``disable`` <identifier>
|
|
||||||
Disable a breakpoint. It remains disabled until you turn it on again
|
|
||||||
with the ``enable`` command.
|
|
||||||
|
|
||||||
``enable`` <identifier>
|
|
||||||
Enable a breakpoint.
|
|
||||||
|
|
||||||
Often it happens when debugging that you keep retyping the breakpoints again
|
|
||||||
and again because they are lost when you restart your program. This is not
|
|
||||||
necessary: A special pragma has been defined for this:
|
|
||||||
|
|
||||||
|
|
||||||
The ``breakpoint`` pragma
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
The ``breakpoint`` pragma is syntactically a statement. It can be used
|
|
||||||
to mark the *following line* as a breakpoint:
|
|
||||||
|
|
||||||
.. code-block:: Nim
|
|
||||||
write("1")
|
|
||||||
{.breakpoint: "before_write_2".}
|
|
||||||
write("2")
|
|
||||||
|
|
||||||
The name of the breakpoint here is ``before_write_2``. Of course the
|
|
||||||
breakpoint's name is optional - the compiler will generate one for you
|
|
||||||
if you leave it out.
|
|
||||||
|
|
||||||
Code for the ``breakpoint`` pragma is only generated if the debugger
|
|
||||||
is turned on, so you don't need to remove it from your source code after
|
|
||||||
debugging.
|
|
||||||
|
|
||||||
|
|
||||||
The ``watchpoint`` pragma
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
The ``watchpoint`` pragma is syntactically a statement. It can be used
|
|
||||||
to mark a location as a watchpoint:
|
|
||||||
|
|
||||||
.. code-block:: Nim
|
|
||||||
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 Nim code line it is checked whether the
|
|
||||||
location changed.
|
|
||||||
|
|
||||||
|
|
||||||
Data Display Commands
|
|
||||||
=====================
|
|
||||||
|
|
||||||
``e``, ``eval`` <exp>
|
|
||||||
Evaluate the expression <exp>. Note that ENDB has no full-blown expression
|
|
||||||
evaluator built-in. So expressions are limited:
|
|
||||||
|
|
||||||
* To display global variables prefix their names with their
|
|
||||||
owning module: ``nim1.globalVar``
|
|
||||||
* To display local variables or parameters just type in
|
|
||||||
their name: ``localVar``. If you want to inspect variables that are not
|
|
||||||
in the current stack frame, use the ``up`` or ``down`` command.
|
|
||||||
|
|
||||||
Unfortunately, only inspecting variables is possible at the moment. Maybe
|
|
||||||
a future version will implement a full-blown Nim expression evaluator,
|
|
||||||
but this is not easy to do and would bloat the debugger's code.
|
|
||||||
|
|
||||||
Since displaying the whole data structures is often not needed and
|
|
||||||
painfully slow, the debugger uses a *maximal display depth* concept for
|
|
||||||
displaying.
|
|
||||||
|
|
||||||
You can alter the maximal display depth with the ``maxdisplay``
|
|
||||||
command.
|
|
||||||
|
|
||||||
``maxdisplay`` <natural>
|
|
||||||
Sets the maximal display depth to the given integer value. A value of 0
|
|
||||||
means there is no maximal display depth. Default is 3.
|
|
||||||
|
|
||||||
``o``, ``out`` <filename> <exp>
|
|
||||||
Evaluate the expression <exp> and store its string representation into a
|
|
||||||
file named <filename>. If the file does not exist, it will be created,
|
|
||||||
otherwise it will be opened for appending.
|
|
||||||
|
|
||||||
``w``, ``where``
|
|
||||||
Display the current execution point.
|
|
||||||
|
|
||||||
``u``, ``up``
|
|
||||||
Go up in the call stack.
|
|
||||||
|
|
||||||
``d``, ``down``
|
|
||||||
Go down in the call stack.
|
|
||||||
|
|
||||||
``stackframe`` [file]
|
|
||||||
Displays the content of the current stack frame in ``stdout`` or
|
|
||||||
appends it to the file, depending on whether a file is given.
|
|
||||||
|
|
||||||
``callstack``
|
|
||||||
Display the entire call stack (but not its content).
|
|
||||||
|
|
||||||
``l``, ``locals``
|
|
||||||
Display the available local variables in the current stack frame.
|
|
||||||
|
|
||||||
``g``, ``globals``
|
|
||||||
Display all the global variables that are available for inspection.
|
|
||||||
|
|
@ -3639,10 +3639,6 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
if result == 0:
|
if result == 0:
|
||||||
result = x.len - y.len
|
result = x.len - y.len
|
||||||
|
|
||||||
when not defined(nimscript) and hostOS != "standalone":
|
|
||||||
when defined(endb):
|
|
||||||
proc endbStep()
|
|
||||||
|
|
||||||
when declared(newSeq):
|
when declared(newSeq):
|
||||||
proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
|
proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
|
||||||
## Converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
|
## Converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be
|
||||||
|
|
@ -3816,9 +3812,6 @@ when not defined(JS): #and not defined(nimscript):
|
||||||
currException = exc
|
currException = exc
|
||||||
|
|
||||||
{.push stack_trace: off, profiler:off.}
|
{.push stack_trace: off, profiler:off.}
|
||||||
when defined(endb) and not defined(nimscript):
|
|
||||||
include "system/debugger"
|
|
||||||
|
|
||||||
when (defined(profiler) or defined(memProfiler)) and not defined(nimscript):
|
when (defined(profiler) or defined(memProfiler)) and not defined(nimscript):
|
||||||
include "system/profiler"
|
include "system/profiler"
|
||||||
{.pop.} # stacktrace
|
{.pop.} # stacktrace
|
||||||
|
|
|
||||||
|
|
@ -1,303 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This file implements basic features for any debugger.
|
|
||||||
|
|
||||||
type
|
|
||||||
VarSlot* {.compilerproc, final.} = object ## a slot in a frame
|
|
||||||
address*: pointer ## the variable's address
|
|
||||||
typ*: PNimType ## the variable's type
|
|
||||||
name*: cstring ## the variable's name; for globals this is "module.name"
|
|
||||||
|
|
||||||
PExtendedFrame = ptr ExtendedFrame
|
|
||||||
ExtendedFrame = object # If the debugger is enabled the compiler
|
|
||||||
# provides an extended frame. Of course
|
|
||||||
# only slots that are
|
|
||||||
# needed are allocated and not 10_000,
|
|
||||||
# except for the global data description.
|
|
||||||
f: TFrame
|
|
||||||
slots: array[0..10_000, VarSlot]
|
|
||||||
|
|
||||||
var
|
|
||||||
dbgGlobalData: ExtendedFrame # this reserves much space, but
|
|
||||||
# for now it is the most practical way
|
|
||||||
|
|
||||||
proc dbgRegisterGlobal(name: cstring, address: pointer,
|
|
||||||
typ: PNimType) {.compilerproc.} =
|
|
||||||
let i = dbgGlobalData.f.len
|
|
||||||
if i >= high(dbgGlobalData.slots):
|
|
||||||
#debugOut("[Warning] cannot register global ")
|
|
||||||
return
|
|
||||||
dbgGlobalData.slots[i].name = name
|
|
||||||
dbgGlobalData.slots[i].typ = typ
|
|
||||||
dbgGlobalData.slots[i].address = address
|
|
||||||
inc(dbgGlobalData.f.len)
|
|
||||||
|
|
||||||
proc getLocal*(frame: PFrame; slot: int): VarSlot {.inline.} =
|
|
||||||
## retrieves the meta data for the local variable at `slot`. CAUTION: An
|
|
||||||
## invalid `slot` value causes a corruption!
|
|
||||||
result = cast[PExtendedFrame](frame).slots[slot]
|
|
||||||
|
|
||||||
proc getGlobalLen*(): int {.inline.} =
|
|
||||||
## gets the number of registered globals.
|
|
||||||
result = dbgGlobalData.f.len
|
|
||||||
|
|
||||||
proc getGlobal*(slot: int): VarSlot {.inline.} =
|
|
||||||
## retrieves the meta data for the global variable at `slot`. CAUTION: An
|
|
||||||
## invalid `slot` value causes a corruption!
|
|
||||||
result = dbgGlobalData.slots[slot]
|
|
||||||
|
|
||||||
# ------------------- breakpoint support ------------------------------------
|
|
||||||
|
|
||||||
type
|
|
||||||
Breakpoint* = object ## represents a break point
|
|
||||||
low*, high*: int ## range from low to high; if disabled
|
|
||||||
## both low and high are set to their negative values
|
|
||||||
filename*: cstring ## the filename of the breakpoint
|
|
||||||
|
|
||||||
var
|
|
||||||
dbgBP: array[0..127, Breakpoint] # breakpoints
|
|
||||||
dbgBPlen: int
|
|
||||||
dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking
|
|
||||||
|
|
||||||
dbgFilenames*: array[0..300, cstring] ## registered filenames;
|
|
||||||
## 'nil' terminated
|
|
||||||
dbgFilenameLen: int
|
|
||||||
|
|
||||||
proc dbgRegisterFilename(filename: cstring) {.compilerproc.} =
|
|
||||||
# XXX we could check for duplicates here for DLL support
|
|
||||||
dbgFilenames[dbgFilenameLen] = filename
|
|
||||||
inc dbgFilenameLen
|
|
||||||
|
|
||||||
proc dbgRegisterBreakpoint(line: int,
|
|
||||||
filename, name: cstring) {.compilerproc.} =
|
|
||||||
let x = dbgBPlen
|
|
||||||
if x >= high(dbgBP):
|
|
||||||
#debugOut("[Warning] cannot register breakpoint")
|
|
||||||
return
|
|
||||||
inc(dbgBPlen)
|
|
||||||
dbgBP[x].filename = filename
|
|
||||||
dbgBP[x].low = line
|
|
||||||
dbgBP[x].high = line
|
|
||||||
dbgBPbloom = dbgBPbloom or line
|
|
||||||
|
|
||||||
proc addBreakpoint*(filename: cstring, lo, hi: int): bool =
|
|
||||||
let x = dbgBPlen
|
|
||||||
if x >= high(dbgBP): return false
|
|
||||||
inc(dbgBPlen)
|
|
||||||
result = true
|
|
||||||
dbgBP[x].filename = filename
|
|
||||||
dbgBP[x].low = lo
|
|
||||||
dbgBP[x].high = hi
|
|
||||||
for line in lo..hi: dbgBPbloom = dbgBPbloom or line
|
|
||||||
|
|
||||||
const
|
|
||||||
FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
|
|
||||||
|
|
||||||
proc fileMatches(c, bp: cstring): bool =
|
|
||||||
# bp = breakpoint filename
|
|
||||||
# c = current filename
|
|
||||||
# we consider it a match if bp is a suffix of c
|
|
||||||
# and the character for the suffix does not exist or
|
|
||||||
# is one of: \ / :
|
|
||||||
# depending on the OS case does not matter!
|
|
||||||
var blen: int = bp.len
|
|
||||||
var clen: int = c.len
|
|
||||||
if blen > clen: return false
|
|
||||||
# check for \ / :
|
|
||||||
if clen-blen-1 >= 0 and c[clen-blen-1] notin {'\\', '/', ':'}:
|
|
||||||
return false
|
|
||||||
var i = 0
|
|
||||||
while i < blen:
|
|
||||||
var x = bp[i]
|
|
||||||
var y = c[i+clen-blen]
|
|
||||||
when FileSystemCaseInsensitive:
|
|
||||||
if x >= 'A' and x <= 'Z': x = chr(ord(x) - ord('A') + ord('a'))
|
|
||||||
if y >= 'A' and y <= 'Z': y = chr(ord(y) - ord('A') + ord('a'))
|
|
||||||
if x != y: return false
|
|
||||||
inc(i)
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc canonFilename*(filename: cstring): cstring =
|
|
||||||
## returns 'nil' if the filename cannot be found.
|
|
||||||
for i in 0 .. dbgFilenameLen-1:
|
|
||||||
result = dbgFilenames[i]
|
|
||||||
if fileMatches(result, filename): return result
|
|
||||||
result = nil
|
|
||||||
|
|
||||||
iterator listBreakpoints*(): ptr Breakpoint =
|
|
||||||
## lists all breakpoints.
|
|
||||||
for i in 0..dbgBPlen-1: yield addr(dbgBP[i])
|
|
||||||
|
|
||||||
proc isActive*(b: ptr Breakpoint): bool = b.low > 0
|
|
||||||
proc flip*(b: ptr Breakpoint) =
|
|
||||||
## enables or disables 'b' depending on its current state.
|
|
||||||
b.low = -b.low; b.high = -b.high
|
|
||||||
|
|
||||||
proc checkBreakpoints*(filename: cstring, line: int): ptr Breakpoint =
|
|
||||||
## in which breakpoint (if any) we are.
|
|
||||||
if (dbgBPbloom and line) != line: return nil
|
|
||||||
for b in listBreakpoints():
|
|
||||||
if line >= b.low and line <= b.high and filename == b.filename: return b
|
|
||||||
|
|
||||||
# ------------------- watchpoint support ------------------------------------
|
|
||||||
|
|
||||||
type
|
|
||||||
Hash = int
|
|
||||||
Watchpoint {.pure, final.} = object
|
|
||||||
name: cstring
|
|
||||||
address: pointer
|
|
||||||
typ: PNimType
|
|
||||||
oldValue: Hash
|
|
||||||
|
|
||||||
var
|
|
||||||
watchpoints: array[0..99, Watchpoint]
|
|
||||||
watchpointsLen: int
|
|
||||||
|
|
||||||
proc `!&`(h: Hash, val: int): Hash {.inline.} =
|
|
||||||
result = h +% val
|
|
||||||
result = result +% result shl 10
|
|
||||||
result = result xor (result shr 6)
|
|
||||||
|
|
||||||
proc `!$`(h: Hash): Hash {.inline.} =
|
|
||||||
result = h +% h shl 3
|
|
||||||
result = result xor (result shr 11)
|
|
||||||
result = result +% result shl 15
|
|
||||||
|
|
||||||
proc hash(data: pointer, size: int): Hash =
|
|
||||||
var h: Hash = 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 hashGcHeader(data: pointer): Hash =
|
|
||||||
const headerSize = sizeof(int)*2
|
|
||||||
result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize)
|
|
||||||
|
|
||||||
proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
|
|
||||||
h: Hash): Hash
|
|
||||||
proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool,
|
|
||||||
h: Hash): Hash =
|
|
||||||
var d = cast[ByteAddress](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: Hash): Hash =
|
|
||||||
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)
|
|
||||||
when defined(trackGcHeaders):
|
|
||||||
result = result !& hashGcHeader(x)
|
|
||||||
else:
|
|
||||||
result = result !& hash(x, s.len)
|
|
||||||
of tySequence:
|
|
||||||
var x = cast[PPointer](dest)
|
|
||||||
var dst = cast[ByteAddress](cast[PPointer](dest)[])
|
|
||||||
result = h
|
|
||||||
if dst != 0:
|
|
||||||
when defined(trackGcHeaders):
|
|
||||||
result = result !& hashGcHeader(cast[PPointer](dest)[])
|
|
||||||
else:
|
|
||||||
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[ByteAddress](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:
|
|
||||||
when defined(trackGcHeaders):
|
|
||||||
var s = cast[PPointer](dest)[]
|
|
||||||
if s != nil:
|
|
||||||
result = result !& hashGcHeader(s)
|
|
||||||
else:
|
|
||||||
if shallow:
|
|
||||||
result = h !& hash(dest, mt.size)
|
|
||||||
else:
|
|
||||||
result = h
|
|
||||||
var s = cast[PPointer](dest)[]
|
|
||||||
if s != nil:
|
|
||||||
result = result !& genericHashAux(s, mt.base, shallow, result)
|
|
||||||
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 .. pred(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 dbgUnregisterWatchpoints*() =
|
|
||||||
watchPointsLen = 0
|
|
||||||
|
|
||||||
var
|
|
||||||
dbgLineHook*: proc () {.nimcall.}
|
|
||||||
## set this variable to provide a procedure that should be called before
|
|
||||||
## each executed instruction. This should only be used by debuggers!
|
|
||||||
## Only code compiled with the ``debugger:on`` switch calls this hook.
|
|
||||||
|
|
||||||
dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.}
|
|
||||||
|
|
||||||
proc checkWatchpoints =
|
|
||||||
let L = watchPointsLen
|
|
||||||
for i in 0 .. pred(L):
|
|
||||||
let newHash = genericHash(watchPoints[i].address, watchPoints[i].typ)
|
|
||||||
if newHash != watchPoints[i].oldValue:
|
|
||||||
dbgWatchpointHook(watchPoints[i].name)
|
|
||||||
watchPoints[i].oldValue = newHash
|
|
||||||
|
|
||||||
proc endb(line: int, file: cstring) {.compilerproc, noinline.} =
|
|
||||||
# This proc is called before every Nim code line!
|
|
||||||
if framePtr == nil: return
|
|
||||||
if dbgWatchpointHook != nil: checkWatchpoints()
|
|
||||||
framePtr.line = line # this is done here for smaller code size!
|
|
||||||
framePtr.filename = file
|
|
||||||
if dbgLineHook != nil: dbgLineHook()
|
|
||||||
|
|
||||||
include "system/endb"
|
|
||||||
|
|
@ -1,579 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
# This file implements the embedded debugger that can be linked
|
|
||||||
# with the application. Mostly we do not use dynamic memory here as that
|
|
||||||
# would interfere with the GC and trigger ON/OFF errors if the
|
|
||||||
# user program corrupts memory. Unfortunately, for dispaying
|
|
||||||
# variables we use the ``system.repr()`` proc which uses Nim
|
|
||||||
# strings and thus allocates memory from the heap. Pity, but
|
|
||||||
# I do not want to implement ``repr()`` twice.
|
|
||||||
|
|
||||||
const
|
|
||||||
EndbBeg = "*** endb"
|
|
||||||
EndbEnd = "***\n"
|
|
||||||
|
|
||||||
type
|
|
||||||
StaticStr = object
|
|
||||||
len: int
|
|
||||||
data: array[0..100, char]
|
|
||||||
|
|
||||||
BreakpointFilename = object
|
|
||||||
b: ptr Breakpoint
|
|
||||||
filename: StaticStr
|
|
||||||
|
|
||||||
DbgState = enum
|
|
||||||
dbOff, # debugger is turned off
|
|
||||||
dbStepInto, # debugger is in tracing mode
|
|
||||||
dbStepOver,
|
|
||||||
dbSkipCurrent,
|
|
||||||
dbQuiting, # debugger wants to quit
|
|
||||||
dbBreakpoints # debugger is only interested in breakpoints
|
|
||||||
|
|
||||||
var
|
|
||||||
dbgUser: StaticStr # buffer for user input; first command is ``step_into``
|
|
||||||
# needs to be global cause we store the last command
|
|
||||||
# in it
|
|
||||||
dbgState: DbgState # state of debugger
|
|
||||||
dbgSkipToFrame: PFrame # frame to be skipped to
|
|
||||||
|
|
||||||
maxDisplayRecDepth: int = 5 # do not display too much data!
|
|
||||||
|
|
||||||
brkPoints: array[0..127, BreakpointFilename]
|
|
||||||
|
|
||||||
proc setLen(s: var StaticStr, newLen=0) =
|
|
||||||
s.len = newLen
|
|
||||||
s.data[newLen] = '\0'
|
|
||||||
|
|
||||||
proc add(s: var StaticStr, 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 StaticStr, c: cstring) =
|
|
||||||
var i = 0
|
|
||||||
while c[i] != '\0':
|
|
||||||
add s, c[i]
|
|
||||||
inc i
|
|
||||||
|
|
||||||
proc assign(s: var StaticStr, c: cstring) =
|
|
||||||
setLen(s)
|
|
||||||
add s, c
|
|
||||||
|
|
||||||
proc `==`(a, b: StaticStr): 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: StaticStr, b: cstring): bool =
|
|
||||||
result = c_strcmp(unsafeAddr a.data, b) == 0
|
|
||||||
|
|
||||||
proc write(f: CFilePtr, s: cstring) = c_fputs(s, f)
|
|
||||||
proc writeLine(f: CFilePtr, s: cstring) =
|
|
||||||
c_fputs(s, f)
|
|
||||||
c_fputs("\n", f)
|
|
||||||
|
|
||||||
proc write(f: CFilePtr, s: StaticStr) =
|
|
||||||
write(f, cstring(unsafeAddr s.data))
|
|
||||||
|
|
||||||
proc write(f: CFilePtr, i: int) =
|
|
||||||
when sizeof(int) == 8:
|
|
||||||
discard c_fprintf(f, "%lld", i)
|
|
||||||
else:
|
|
||||||
discard c_fprintf(f, "%ld", i)
|
|
||||||
|
|
||||||
proc close(f: CFilePtr): cint {.
|
|
||||||
importc: "fclose", header: "<stdio.h>", discardable.}
|
|
||||||
|
|
||||||
proc c_fgetc(stream: CFilePtr): cint {.
|
|
||||||
importc: "fgetc", header: "<stdio.h>".}
|
|
||||||
proc c_ungetc(c: cint, f: CFilePtr): cint {.
|
|
||||||
importc: "ungetc", header: "<stdio.h>", discardable.}
|
|
||||||
|
|
||||||
var
|
|
||||||
cstdin* {.importc: "stdin", header: "<stdio.h>".}: CFilePtr
|
|
||||||
|
|
||||||
proc listBreakPoints() =
|
|
||||||
write(cstdout, EndbBeg)
|
|
||||||
write(cstdout, "| Breakpoints:\n")
|
|
||||||
for b in listBreakpoints():
|
|
||||||
write(cstdout, abs(b.low))
|
|
||||||
if b.high != b.low:
|
|
||||||
write(cstdout, "..")
|
|
||||||
write(cstdout, abs(b.high))
|
|
||||||
write(cstdout, " ")
|
|
||||||
write(cstdout, b.filename)
|
|
||||||
if b.isActive:
|
|
||||||
write(cstdout, " [disabled]\n")
|
|
||||||
else:
|
|
||||||
write(cstdout, "\n")
|
|
||||||
write(cstdout, EndbEnd)
|
|
||||||
|
|
||||||
proc openAppend(filename: cstring): CFilePtr =
|
|
||||||
proc fopen(filename, mode: cstring): CFilePtr {.importc: "fopen", header: "<stdio.h>".}
|
|
||||||
|
|
||||||
result = fopen(filename, "ab")
|
|
||||||
if result != nil:
|
|
||||||
write(result, "----------------------------------------\n")
|
|
||||||
|
|
||||||
proc dbgRepr(p: pointer, typ: PNimType): string =
|
|
||||||
var cl: ReprClosure
|
|
||||||
initReprClosure(cl)
|
|
||||||
cl.recDepth = maxDisplayRecDepth
|
|
||||||
# locks for the GC turned out to be a bad idea...
|
|
||||||
# inc(recGcLock)
|
|
||||||
result = ""
|
|
||||||
reprAux(result, p, typ, cl)
|
|
||||||
# dec(recGcLock)
|
|
||||||
deinitReprClosure(cl)
|
|
||||||
|
|
||||||
proc writeVariable(stream: CFilePtr, slot: VarSlot) =
|
|
||||||
write(stream, slot.name)
|
|
||||||
write(stream, " = ")
|
|
||||||
writeLine(stream, dbgRepr(slot.address, slot.typ))
|
|
||||||
|
|
||||||
proc listFrame(stream: CFilePtr, f: PFrame) =
|
|
||||||
write(stream, EndbBeg)
|
|
||||||
write(stream, "| Frame (")
|
|
||||||
write(stream, f.len)
|
|
||||||
write(stream, " slots):\n")
|
|
||||||
for i in 0 .. f.len-1:
|
|
||||||
writeLine(stream, getLocal(f, i).name)
|
|
||||||
write(stream, EndbEnd)
|
|
||||||
|
|
||||||
proc listLocals(stream: CFilePtr, f: PFrame) =
|
|
||||||
write(stream, EndbBeg)
|
|
||||||
write(stream, "| Frame (")
|
|
||||||
write(stream, f.len)
|
|
||||||
write(stream, " slots):\n")
|
|
||||||
for i in 0 .. f.len-1:
|
|
||||||
writeVariable(stream, getLocal(f, i))
|
|
||||||
write(stream, EndbEnd)
|
|
||||||
|
|
||||||
proc listGlobals(stream: CFilePtr) =
|
|
||||||
write(stream, EndbBeg)
|
|
||||||
write(stream, "| Globals:\n")
|
|
||||||
for i in 0 .. getGlobalLen()-1:
|
|
||||||
writeLine(stream, getGlobal(i).name)
|
|
||||||
write(stream, EndbEnd)
|
|
||||||
|
|
||||||
proc debugOut(msg: cstring) =
|
|
||||||
# the *** *** markers are for easy recognition of debugger
|
|
||||||
# output for external frontends.
|
|
||||||
write(cstdout, EndbBeg)
|
|
||||||
write(cstdout, "| ")
|
|
||||||
write(cstdout, msg)
|
|
||||||
write(cstdout, EndbEnd)
|
|
||||||
|
|
||||||
proc dbgFatal(msg: cstring) =
|
|
||||||
debugOut(msg)
|
|
||||||
dbgAborting = true # the debugger wants to abort
|
|
||||||
quit(1)
|
|
||||||
|
|
||||||
proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
|
|
||||||
if dbgFramePointer != nil:
|
|
||||||
write(cstdout, "*** endb| now in proc: ")
|
|
||||||
write(cstdout, dbgFramePointer.procname)
|
|
||||||
write(cstdout, " ***\n")
|
|
||||||
else:
|
|
||||||
write(cstdout, "*** endb| (proc name not available) ***\n")
|
|
||||||
|
|
||||||
proc dbgShowExecutionPoint() =
|
|
||||||
write(cstdout, "*** endb| ")
|
|
||||||
write(cstdout, framePtr.filename)
|
|
||||||
write(cstdout, "(")
|
|
||||||
write(cstdout, framePtr.line)
|
|
||||||
write(cstdout, ") ")
|
|
||||||
write(cstdout, framePtr.procname)
|
|
||||||
write(cstdout, " ***\n")
|
|
||||||
|
|
||||||
proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int =
|
|
||||||
result = start
|
|
||||||
# skip whitespace:
|
|
||||||
while src[result] in {'\t', ' '}: inc(result)
|
|
||||||
while true:
|
|
||||||
case src[result]
|
|
||||||
of 'a'..'z', '0'..'9': add(a, src[result])
|
|
||||||
of '_': discard # just skip it
|
|
||||||
of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a')))
|
|
||||||
else: break
|
|
||||||
inc(result)
|
|
||||||
|
|
||||||
proc scanWord(src: cstring, a: var StaticStr, start: int): int =
|
|
||||||
setlen(a)
|
|
||||||
result = scanAndAppendWord(src, a, start)
|
|
||||||
|
|
||||||
proc scanFilename(src: cstring, a: var StaticStr, start: int): int =
|
|
||||||
result = start
|
|
||||||
setLen a
|
|
||||||
while src[result] in {'\t', ' '}: inc(result)
|
|
||||||
while src[result] notin {'\t', ' ', '\0'}:
|
|
||||||
add(a, src[result])
|
|
||||||
inc(result)
|
|
||||||
|
|
||||||
proc scanNumber(src: cstring, a: var int, start: int): int =
|
|
||||||
result = start
|
|
||||||
a = 0
|
|
||||||
while src[result] in {'\t', ' '}: inc(result)
|
|
||||||
while true:
|
|
||||||
case src[result]
|
|
||||||
of '0'..'9': a = a * 10 + ord(src[result]) - ord('0')
|
|
||||||
of '_': discard # skip underscores (nice for long line numbers)
|
|
||||||
else: break
|
|
||||||
inc(result)
|
|
||||||
|
|
||||||
proc dbgHelp() =
|
|
||||||
debugOut("""
|
|
||||||
list of commands (see the manual for further help):
|
|
||||||
GENERAL
|
|
||||||
h, help display this help message
|
|
||||||
q, quit quit the debugger and the program
|
|
||||||
<ENTER> repeat the previous debugger command
|
|
||||||
EXECUTING
|
|
||||||
s, step single step, stepping into routine calls
|
|
||||||
n, next single step, without stepping into routine calls
|
|
||||||
f, skipcurrent continue execution until the current routine finishes
|
|
||||||
c, continue, r, run continue execution until the next breakpoint
|
|
||||||
i, ignore continue execution, ignore all breakpoints
|
|
||||||
BREAKPOINTS
|
|
||||||
b, break [fromline [toline]] [file]
|
|
||||||
set a new breakpoint for line and file
|
|
||||||
if line or file are omitted the current one is used
|
|
||||||
breakpoints display the entire breakpoint list
|
|
||||||
toggle fromline [file] enable or disable a breakpoint
|
|
||||||
filenames list all valid filenames
|
|
||||||
DATA DISPLAY
|
|
||||||
e, eval <expr> evaluate the expression <expr>
|
|
||||||
o, out <file> <expr> evaluate <expr> and write it to <file>
|
|
||||||
w, where display the current execution point
|
|
||||||
stackframe [file] display current stack frame [and write it to file]
|
|
||||||
u, up go up in the call stack
|
|
||||||
d, down go down in the call stack
|
|
||||||
bt, backtrace display the entire call stack
|
|
||||||
l, locals display available local variables
|
|
||||||
g, globals display available global variables
|
|
||||||
maxdisplay <integer> set the display's recursion maximum
|
|
||||||
""")
|
|
||||||
|
|
||||||
proc invalidCommand() =
|
|
||||||
debugOut("[Warning] invalid command ignored (type 'h' for help) ")
|
|
||||||
|
|
||||||
proc hasExt(s: cstring): bool =
|
|
||||||
# returns true if s has a filename extension
|
|
||||||
var i = 0
|
|
||||||
while s[i] != '\0':
|
|
||||||
if s[i] == '.': return true
|
|
||||||
inc i
|
|
||||||
|
|
||||||
proc parseBreakpoint(s: cstring, start: int): Breakpoint =
|
|
||||||
var dbgTemp: StaticStr
|
|
||||||
var i = scanNumber(s, result.low, start)
|
|
||||||
if result.low == 0: result.low = framePtr.line
|
|
||||||
i = scanNumber(s, result.high, i)
|
|
||||||
if result.high == 0: result.high = result.low
|
|
||||||
i = scanFilename(s, dbgTemp, i)
|
|
||||||
if dbgTemp.len != 0:
|
|
||||||
if not hasExt(addr dbgTemp.data): add(dbgTemp, ".nim")
|
|
||||||
result.filename = canonFilename(addr dbgTemp.data)
|
|
||||||
if result.filename.isNil:
|
|
||||||
debugOut("[Warning] no breakpoint could be set; unknown filename ")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
result.filename = framePtr.filename
|
|
||||||
|
|
||||||
proc createBreakPoint(s: cstring, start: int) =
|
|
||||||
let br = parseBreakpoint(s, start)
|
|
||||||
if not br.filename.isNil:
|
|
||||||
if not addBreakpoint(br.filename, br.low, br.high):
|
|
||||||
debugOut("[Warning] no breakpoint could be set; out of breakpoint space ")
|
|
||||||
|
|
||||||
proc breakpointToggle(s: cstring, start: int) =
|
|
||||||
var a = parseBreakpoint(s, start)
|
|
||||||
if not a.filename.isNil:
|
|
||||||
var b = checkBreakpoints(a.filename, a.low)
|
|
||||||
if not b.isNil: b.flip
|
|
||||||
else: debugOut("[Warning] unknown breakpoint ")
|
|
||||||
|
|
||||||
proc dbgEvaluate(stream: CFilePtr, s: cstring, start: int, f: PFrame) =
|
|
||||||
var dbgTemp: StaticStr
|
|
||||||
var i = scanWord(s, dbgTemp, start)
|
|
||||||
while s[i] in {' ', '\t'}: inc(i)
|
|
||||||
var v: VarSlot
|
|
||||||
if s[i] == '.':
|
|
||||||
inc(i)
|
|
||||||
add(dbgTemp, '.')
|
|
||||||
i = scanAndAppendWord(s, dbgTemp, i)
|
|
||||||
for i in 0 .. getGlobalLen()-1:
|
|
||||||
let v = getGlobal(i)
|
|
||||||
if c_strcmp(v.name, addr dbgTemp.data) == 0:
|
|
||||||
writeVariable(stream, v)
|
|
||||||
else:
|
|
||||||
for i in 0 .. f.len-1:
|
|
||||||
let v = getLocal(f, i)
|
|
||||||
if c_strcmp(v.name, addr dbgTemp.data) == 0:
|
|
||||||
writeVariable(stream, v)
|
|
||||||
|
|
||||||
proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
|
|
||||||
var dbgTemp: StaticStr
|
|
||||||
var i = scanFilename(s, dbgTemp, start)
|
|
||||||
if dbgTemp.len == 0:
|
|
||||||
invalidCommand()
|
|
||||||
return
|
|
||||||
var stream = openAppend(addr dbgTemp.data)
|
|
||||||
if stream == nil:
|
|
||||||
debugOut("[Warning] could not open or create file ")
|
|
||||||
return
|
|
||||||
dbgEvaluate(stream, s, i, currFrame)
|
|
||||||
close(stream)
|
|
||||||
|
|
||||||
proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
|
|
||||||
var dbgTemp: StaticStr
|
|
||||||
var i = scanFilename(s, dbgTemp, start)
|
|
||||||
if dbgTemp.len == 0:
|
|
||||||
# just write it to cstdout:
|
|
||||||
listFrame(cstdout, currFrame)
|
|
||||||
else:
|
|
||||||
var stream = openAppend(addr dbgTemp.data)
|
|
||||||
if stream == nil:
|
|
||||||
debugOut("[Warning] could not open or create file ")
|
|
||||||
return
|
|
||||||
listFrame(stream, currFrame)
|
|
||||||
close(stream)
|
|
||||||
|
|
||||||
proc readLine(f: CFilePtr, line: var StaticStr): bool =
|
|
||||||
while true:
|
|
||||||
var c = 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 = c_fgetc(f) # is the next char LF?
|
|
||||||
if c != 10'i32: discard c_ungetc(c, f) # no, put the character back
|
|
||||||
break
|
|
||||||
add line, chr(int(c))
|
|
||||||
result = true
|
|
||||||
|
|
||||||
proc listFilenames() =
|
|
||||||
write(cstdout, EndbBeg)
|
|
||||||
write(cstdout, "| Files:\n")
|
|
||||||
var i = 0
|
|
||||||
while true:
|
|
||||||
let x = dbgFilenames[i]
|
|
||||||
if x.isNil: break
|
|
||||||
write(cstdout, x)
|
|
||||||
write(cstdout, "\n")
|
|
||||||
inc i
|
|
||||||
write(cstdout, EndbEnd)
|
|
||||||
|
|
||||||
proc dbgWriteStackTrace(f: PFrame)
|
|
||||||
proc commandPrompt() =
|
|
||||||
# if we return from this routine, user code executes again
|
|
||||||
var
|
|
||||||
again = true
|
|
||||||
dbgFramePtr = framePtr # for going down and up the stack
|
|
||||||
dbgDown = 0 # how often we did go down
|
|
||||||
dbgTemp: StaticStr
|
|
||||||
|
|
||||||
while again:
|
|
||||||
write(cstdout, "*** endb| >>")
|
|
||||||
let oldLen = dbgUser.len
|
|
||||||
dbgUser.len = 0
|
|
||||||
if not readLine(cstdin, dbgUser): break
|
|
||||||
if dbgUser.len == 0: dbgUser.len = oldLen
|
|
||||||
# now look what we have to do:
|
|
||||||
var i = scanWord(addr dbgUser.data, dbgTemp, 0)
|
|
||||||
template `?`(x: untyped): untyped = dbgTemp == cstring(x)
|
|
||||||
if ?"s" or ?"step":
|
|
||||||
dbgState = dbStepInto
|
|
||||||
again = false
|
|
||||||
elif ?"n" or ?"next":
|
|
||||||
dbgState = dbStepOver
|
|
||||||
dbgSkipToFrame = framePtr
|
|
||||||
again = false
|
|
||||||
elif ?"f" or ?"skipcurrent":
|
|
||||||
dbgState = dbSkipCurrent
|
|
||||||
dbgSkipToFrame = framePtr.prev
|
|
||||||
again = false
|
|
||||||
elif ?"c" or ?"continue" or ?"r" or ?"run":
|
|
||||||
dbgState = dbBreakpoints
|
|
||||||
again = false
|
|
||||||
elif ?"i" or ?"ignore":
|
|
||||||
dbgState = dbOff
|
|
||||||
again = false
|
|
||||||
elif ?"h" or ?"help":
|
|
||||||
dbgHelp()
|
|
||||||
elif ?"q" or ?"quit":
|
|
||||||
dbgState = dbQuiting
|
|
||||||
dbgAborting = true
|
|
||||||
again = false
|
|
||||||
quit(1) # BUGFIX: quit with error code > 0
|
|
||||||
elif ?"e" or ?"eval":
|
|
||||||
var
|
|
||||||
prevState = dbgState
|
|
||||||
prevSkipFrame = dbgSkipToFrame
|
|
||||||
dbgState = dbSkipCurrent
|
|
||||||
dbgEvaluate(cstdout, addr dbgUser.data, i, dbgFramePtr)
|
|
||||||
dbgState = prevState
|
|
||||||
dbgSkipToFrame = prevSkipFrame
|
|
||||||
elif ?"o" or ?"out":
|
|
||||||
dbgOut(addr dbgUser.data, i, dbgFramePtr)
|
|
||||||
elif ?"stackframe":
|
|
||||||
dbgStackFrame(addr dbgUser.data, i, dbgFramePtr)
|
|
||||||
elif ?"w" or ?"where":
|
|
||||||
dbgShowExecutionPoint()
|
|
||||||
elif ?"l" or ?"locals":
|
|
||||||
var
|
|
||||||
prevState = dbgState
|
|
||||||
prevSkipFrame = dbgSkipToFrame
|
|
||||||
dbgState = dbSkipCurrent
|
|
||||||
listLocals(cstdout, dbgFramePtr)
|
|
||||||
dbgState = prevState
|
|
||||||
dbgSkipToFrame = prevSkipFrame
|
|
||||||
elif ?"g" or ?"globals":
|
|
||||||
var
|
|
||||||
prevState = dbgState
|
|
||||||
prevSkipFrame = dbgSkipToFrame
|
|
||||||
dbgState = dbSkipCurrent
|
|
||||||
listGlobals(cstdout)
|
|
||||||
dbgState = prevState
|
|
||||||
dbgSkipToFrame = prevSkipFrame
|
|
||||||
elif ?"u" or ?"up":
|
|
||||||
if dbgDown <= 0:
|
|
||||||
debugOut("[Warning] cannot go up any further ")
|
|
||||||
else:
|
|
||||||
dbgFramePtr = framePtr
|
|
||||||
for j in 0 .. dbgDown-2: # BUGFIX
|
|
||||||
dbgFramePtr = dbgFramePtr.prev
|
|
||||||
dec(dbgDown)
|
|
||||||
dbgShowCurrentProc(dbgFramePtr)
|
|
||||||
elif ?"d" or ?"down":
|
|
||||||
if dbgFramePtr != nil:
|
|
||||||
inc(dbgDown)
|
|
||||||
dbgFramePtr = dbgFramePtr.prev
|
|
||||||
dbgShowCurrentProc(dbgFramePtr)
|
|
||||||
else:
|
|
||||||
debugOut("[Warning] cannot go down any further ")
|
|
||||||
elif ?"bt" or ?"backtrace":
|
|
||||||
dbgWriteStackTrace(framePtr)
|
|
||||||
elif ?"b" or ?"break":
|
|
||||||
createBreakPoint(addr dbgUser.data, i)
|
|
||||||
elif ?"breakpoints":
|
|
||||||
listBreakPoints()
|
|
||||||
elif ?"toggle":
|
|
||||||
breakpointToggle(addr dbgUser.data, i)
|
|
||||||
elif ?"filenames":
|
|
||||||
listFilenames()
|
|
||||||
elif ?"maxdisplay":
|
|
||||||
var parsed: int
|
|
||||||
i = scanNumber(addr dbgUser.data, parsed, i)
|
|
||||||
if dbgUser.data[i-1] in {'0'..'9'}:
|
|
||||||
if parsed == 0: maxDisplayRecDepth = -1
|
|
||||||
else: maxDisplayRecDepth = parsed
|
|
||||||
else:
|
|
||||||
invalidCommand()
|
|
||||||
else: invalidCommand()
|
|
||||||
|
|
||||||
proc endbStep() =
|
|
||||||
# we get into here if an unhandled exception has been raised
|
|
||||||
# XXX: do not allow the user to run the program any further?
|
|
||||||
# XXX: BUG: the frame is lost here!
|
|
||||||
dbgShowExecutionPoint()
|
|
||||||
commandPrompt()
|
|
||||||
|
|
||||||
proc dbgWriteStackTrace(f: PFrame) =
|
|
||||||
const
|
|
||||||
firstCalls = 32
|
|
||||||
var
|
|
||||||
it = f
|
|
||||||
i = 0
|
|
||||||
total = 0
|
|
||||||
tempFrames: array[0..127, PFrame]
|
|
||||||
# setup long head:
|
|
||||||
while it != nil and i <= high(tempFrames)-firstCalls:
|
|
||||||
tempFrames[i] = it
|
|
||||||
inc(i)
|
|
||||||
inc(total)
|
|
||||||
it = it.prev
|
|
||||||
# go up the stack to count 'total':
|
|
||||||
var b = it
|
|
||||||
while it != nil:
|
|
||||||
inc(total)
|
|
||||||
it = it.prev
|
|
||||||
var skipped = 0
|
|
||||||
if total > len(tempFrames):
|
|
||||||
# skip N
|
|
||||||
skipped = total-i-firstCalls+1
|
|
||||||
for j in 1..skipped:
|
|
||||||
if b != nil: b = b.prev
|
|
||||||
# create '...' entry:
|
|
||||||
tempFrames[i] = nil
|
|
||||||
inc(i)
|
|
||||||
# setup short tail:
|
|
||||||
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(cstdout, "(")
|
|
||||||
write(cstdout, skipped)
|
|
||||||
write(cstdout, " calls omitted) ...")
|
|
||||||
else:
|
|
||||||
write(cstdout, tempFrames[j].filename)
|
|
||||||
if tempFrames[j].line > 0:
|
|
||||||
write(cstdout, "(")
|
|
||||||
write(cstdout, tempFrames[j].line)
|
|
||||||
write(cstdout, ")")
|
|
||||||
write(cstdout, " ")
|
|
||||||
write(cstdout, tempFrames[j].procname)
|
|
||||||
write(cstdout, "\n")
|
|
||||||
|
|
||||||
proc checkForBreakpoint =
|
|
||||||
let b = checkBreakpoints(framePtr.filename, framePtr.line)
|
|
||||||
if b != nil:
|
|
||||||
write(cstdout, "*** endb| reached ")
|
|
||||||
write(cstdout, framePtr.filename)
|
|
||||||
write(cstdout, "(")
|
|
||||||
write(cstdout, framePtr.line)
|
|
||||||
write(cstdout, ") ")
|
|
||||||
write(cstdout, framePtr.procname)
|
|
||||||
write(cstdout, " ***\n")
|
|
||||||
commandPrompt()
|
|
||||||
|
|
||||||
proc lineHookImpl() {.nimcall.} =
|
|
||||||
case dbgState
|
|
||||||
of dbStepInto:
|
|
||||||
# we really want the command prompt here:
|
|
||||||
dbgShowExecutionPoint()
|
|
||||||
commandPrompt()
|
|
||||||
of dbSkipCurrent, dbStepOver: # skip current routine
|
|
||||||
if framePtr == dbgSkipToFrame:
|
|
||||||
dbgShowExecutionPoint()
|
|
||||||
commandPrompt()
|
|
||||||
else:
|
|
||||||
# breakpoints are wanted though (I guess)
|
|
||||||
checkForBreakpoint()
|
|
||||||
of dbBreakpoints:
|
|
||||||
# debugger is only interested in breakpoints
|
|
||||||
checkForBreakpoint()
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc watchpointHookImpl(name: cstring) {.nimcall.} =
|
|
||||||
dbgWriteStackTrace(framePtr)
|
|
||||||
debugOut(name)
|
|
||||||
|
|
||||||
proc initDebugger {.inline.} =
|
|
||||||
dbgState = dbStepInto
|
|
||||||
dbgUser.len = 1
|
|
||||||
dbgUser.data[0] = 's'
|
|
||||||
dbgWatchpointHook = watchpointHookImpl
|
|
||||||
dbgLineHook = lineHookImpl
|
|
||||||
|
|
@ -38,9 +38,6 @@ proc showErrorMessage(data: cstring) {.gcsafe.} =
|
||||||
writeToStdErr(data)
|
writeToStdErr(data)
|
||||||
|
|
||||||
proc quitOrDebug() {.inline.} =
|
proc quitOrDebug() {.inline.} =
|
||||||
when defined(endb):
|
|
||||||
endbStep() # call the debugger
|
|
||||||
else:
|
|
||||||
quit(1)
|
quit(1)
|
||||||
|
|
||||||
proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
|
proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
|
||||||
|
|
@ -469,10 +466,6 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl.} =
|
||||||
framePtr = s
|
framePtr = s
|
||||||
if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
|
if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
|
||||||
|
|
||||||
when defined(endb):
|
|
||||||
var
|
|
||||||
dbgAborting: bool # whether the debugger wants to abort
|
|
||||||
|
|
||||||
when defined(cpp) and appType != "lib" and
|
when defined(cpp) and appType != "lib" and
|
||||||
not defined(js) and not defined(nimscript) and
|
not defined(js) and not defined(nimscript) and
|
||||||
hostOS != "standalone" and not defined(noCppExceptions):
|
hostOS != "standalone" and not defined(noCppExceptions):
|
||||||
|
|
@ -515,8 +508,6 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||||
elif s == SIGSEGV:
|
elif s == SIGSEGV:
|
||||||
action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
|
action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
|
||||||
elif s == SIGABRT:
|
elif s == SIGABRT:
|
||||||
when defined(endb):
|
|
||||||
if dbgAborting: return # the debugger wants to abort
|
|
||||||
action("SIGABRT: Abnormal termination.\n")
|
action("SIGABRT: Abnormal termination.\n")
|
||||||
elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
|
elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
|
||||||
elif s == SIGILL: action("SIGILL: Illegal operation.\n")
|
elif s == SIGILL: action("SIGILL: Illegal operation.\n")
|
||||||
|
|
@ -546,7 +537,6 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||||
msg = y
|
msg = y
|
||||||
processSignal(sign, asgn)
|
processSignal(sign, asgn)
|
||||||
showErrorMessage(msg)
|
showErrorMessage(msg)
|
||||||
when defined(endb): dbgAborting = true
|
|
||||||
quit(1) # always quit when SIGABRT
|
quit(1) # always quit when SIGABRT
|
||||||
|
|
||||||
proc registerSignalHandler() =
|
proc registerSignalHandler() =
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,11 @@ method eval(e: ref TPlusExpr): int =
|
||||||
|
|
||||||
proc newLit(x: int): ref TLiteral =
|
proc newLit(x: int): ref TLiteral =
|
||||||
new(result)
|
new(result)
|
||||||
{.watchpoint: result.}
|
|
||||||
result.x = x
|
result.x = x
|
||||||
result.op1 = $getOccupiedMem()
|
result.op1 = $getOccupiedMem()
|
||||||
|
|
||||||
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
|
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
|
||||||
new(result)
|
new(result)
|
||||||
{.watchpoint: result.}
|
|
||||||
result.a = a
|
result.a = a
|
||||||
result.b = b
|
result.b = b
|
||||||
result.op2 = $getOccupiedMem()
|
result.op2 = $getOccupiedMem()
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ echo(["a", "b", "c", "d"].len)
|
||||||
for x in items(["What's", "your", "name", "?", ]):
|
for x in items(["What's", "your", "name", "?", ]):
|
||||||
echo(x)
|
echo(x)
|
||||||
var `name` = readLine(stdin)
|
var `name` = readLine(stdin)
|
||||||
{.breakpoint.}
|
|
||||||
echo("Hi " & thallo.name & "!\n")
|
echo("Hi " & thallo.name & "!\n")
|
||||||
debug(name)
|
debug(name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ block tobject2:
|
||||||
z: int # added a field
|
z: int # added a field
|
||||||
|
|
||||||
proc getPoint( p: var TPoint2d) =
|
proc getPoint( p: var TPoint2d) =
|
||||||
{.breakpoint.}
|
|
||||||
writeLine(stdout, p.x)
|
writeLine(stdout, p.x)
|
||||||
|
|
||||||
var p: TPoint3d
|
var p: TPoint3d
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue