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

@ -35,12 +35,14 @@ proc reverse*[T](a: var openArray[T]) =
reverse(a, 0, a.high)
const
onlySafeCode = false
onlySafeCode = true
proc merge[T](a, b: var openArray[T], lo, m, hi: int,
cmp: proc (x, y: T): int, order: TSortOrder) =
template `<-` (a, b: expr) =
when onlySafeCode:
when true:
a = b
elif onlySafeCode:
shallowCopy(a, b)
else:
copyMem(addr(a), addr(b), sizeof(T))

View file

@ -14,8 +14,6 @@
## explicitly. Because of this there cannot be a user-defined module named
## ``system``.
{.push hints: off.}
type
int* {.magic: Int.} ## default integer type; bitwidth depends on
## architecture, but is always the same as a pointer
@ -35,9 +33,15 @@ type
string* {.magic: String.} ## built-in string type
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
pointer* {.magic: Pointer.} ## built-in pointer type
Ordinal* {.magic: Ordinal.}[T]
const
on* = true ## alias for ``true``
off* = false ## alias for ``false``
{.push hints: off.}
type
Ordinal* {.magic: Ordinal.}[T]
`nil` {.magic: "Nil".}
expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
@ -831,10 +835,16 @@ proc quit*(errorcode: int = QuitSuccess) {.
## It does *not* call the garbage collector to free all the memory,
## unless a quit procedure calls ``GC_collect``.
proc WriteStackTrace()
var checkDisabled: bool
template sysAssert(cond, msg: expr) =
when defined(useSysAssert):
if not cond:
if not checkDisabled and not cond:
checkDisabled = true
echo "[SYSASSERT] ", msg
WriteStackTrace()
quit 1
nil
@ -1124,8 +1134,10 @@ proc `$` *[T](x: ordinal[T]): string {.magic: "EnumToStr", noSideEffect.}
# undocumented:
proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.}
proc getRefcount*(x: string): int {.importc: "getRefcount", noSideEffect.}
proc getRefcount*[T](x: seq[T]): int {.importc: "getRefcount", noSideEffect.}
## retrieves the reference count of an heap-allocated object. The
## value is implementation-dependant.
## value is implementation-dependent.
# new constants:
const
@ -1459,7 +1471,7 @@ template accumulateResult*(iter: expr) =
# we have to compute this here before turning it off in except.nim anyway ...
const nimrodStackTrace = compileOption("stacktrace")
{.push checks: off, debugger: off.}
{.push checks: off.}
# obviously we cannot generate checking operations here :-)
# because it would yield into an endless recursion
# however, stack-traces are available for most parts
@ -1516,8 +1528,8 @@ type
len: int # length of slots (when not debugging always zero)
when not defined(ECMAScript):
{.push stack_trace:off}
proc add*(x: var string, y: cstring) =
{.push stack_trace:off.}
proc add*(x: var string, y: cstring) {.noStackFrame.} =
var i = 0
while y[i] != '\0':
add(x, y[i])
@ -1600,7 +1612,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
when defined(endb):
proc endbStep()
# ----------------- IO Part --------------------------------------------------
# ----------------- IO Part ------------------------------------------------
type
CFile {.importc: "FILE", nodecl, final.} = object # empty record for
@ -1783,7 +1795,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
while a[L] != nil: inc(L)
result = cstringArrayToSeq(a, L)
# ----------------------------------------------------------------------------
# -------------------------------------------------------------------------
proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
## atomic increment of `memLoc`. Returns the value after the operation.
@ -1809,11 +1821,13 @@ when not defined(EcmaScript) and not defined(NimrodVM):
initStackBottom()
initGC()
{.push stack_trace: off.}
include "system/excpt"
# we cannot compile this with stack tracing on
# as it would recurse endlessly!
include "system/arithm"
{.pop.} # stack trace
{.pop.} # stack trace
include "system/dyncalls"
include "system/sets"
@ -1843,8 +1857,8 @@ when not defined(EcmaScript) and not defined(NimrodVM):
else:
result = n.sons[n.len]
{.push stack_trace: off.}
include "system/mmdisp"
{.push stack_trace: off.}
include "system/sysstr"
{.pop.}
@ -2109,3 +2123,7 @@ template doAssert*(cond: expr, msg = "") =
{.line: InstantiationInfo().}:
raiseAssert(astToStr(cond) & ' ' & msg)
when defined(initDebugger):
initDebugger()

View file

@ -499,6 +499,7 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
sysAssert(roundup(65, 8) == 72, "rawAlloc 1")
sysAssert requestedSize >= sizeof(TFreeCell), "rawAlloc 2"
var size = roundup(requestedSize, MemAlign)
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
if size <= SmallChunkSize-smallChunkOverhead():
# allocate a small block: for small chunks, we use only its next pointer
@ -561,6 +562,7 @@ proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
zeroMem(result, requestedSize)
proc rawDealloc(a: var TMemRegion, p: pointer) =
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
sysAssert(allocInv(a), "rawDealloc: begin")
var c = pageAddr(p)
if isSmallChunk(c):

View file

@ -13,7 +13,7 @@ type
TCell {.pure.} = object
refcount: int # the refcount and some flags
typ: PNimType
when debugGC:
when leakDetector:
filename: cstring
line: int

View file

@ -8,15 +8,18 @@
#
# This file implements the embedded debugger that can be linked
# with the application. We should not use dynamic memory here as that
# with the application. Mostly we do not use dynamic memory here as that
# would interfere with the GC and trigger ON/OFF errors if the
# user program corrupts memory. Unfortunately, for dispaying
# variables we use the ``system.repr()`` proc which uses Nimrod
# strings and thus allocates memory from the heap. Pity, but
# I do not want to implement ``repr()`` twice. We also cannot deactivate
# the GC here as that might run out of memory too quickly...
# I do not want to implement ``repr()`` twice.
type
TStaticStr {.pure, final.} = object
len: int
data: array[0..100, char]
TDbgState = enum
dbOff, # debugger is turned off
dbStepInto, # debugger is in tracing mode
@ -29,8 +32,8 @@ type
low, high: int # range from low to high; if disabled
# both low and high are set to their negative values
# this makes the check faster and safes memory
filename: string
name: string # name of breakpoint
filename: cstring
name: TStaticStr # name of breakpoint
TVarSlot {.compilerproc, final.} = object # variable slots used for debugger:
address: pointer
@ -47,12 +50,10 @@ type
slots: array[0..10_000, TVarSlot]
var
dbgInSignal: bool # wether the debugger is in the signal handler
dbgIn: TFile # debugger input stream
dbgUser: string = "s" # buffer for user input; first command is ``step_into``
dbgUser: TStaticStr # buffer for user input; first command is ``step_into``
# needs to be global cause we store the last command
# in it
dbgState: TDbgState = dbStepInto # state of debugger
dbgState: TDbgState # state of debugger
dbgBP: array[0..127, TDbgBreakpoint] # breakpoints
dbgBPlen: int = 0
@ -63,7 +64,36 @@ var
maxDisplayRecDepth: int = 5 # do not display too much data!
proc findBreakpoint(name: string): int =
proc setLen(s: var TStaticStr, newLen=0) =
s.len = newLen
s.data[newLen] = '\0'
proc add(s: var TStaticStr, c: char) =
if s.len < high(s.data)-1:
s.data[s.len] = c
s.data[s.len+1] = '\0'
inc s.len
proc add(s: var TStaticStr, c: cstring) =
var i = 0
while c[i] != '\0':
add s, c[i]
inc i
proc assign(s: var TStaticStr, c: cstring) =
setLen(s)
add s, c
proc `==`(a, b: TStaticStr): bool =
if a.len == b.len:
for i in 0 .. a.len-1:
if a.data[i] != b.data[i]: return false
return true
proc `==`(a: TStaticStr, b: cstring): bool =
result = c_strcmp(a.data, b) == 0
proc findBreakpoint(name: TStaticStr): int =
# returns -1 if not found
for i in countdown(dbgBPlen-1, 0):
if name == dbgBP[i].name: return i
@ -72,16 +102,22 @@ proc findBreakpoint(name: string): int =
proc ListBreakPoints() =
write(stdout, "*** endb| Breakpoints:\n")
for i in 0 .. dbgBPlen-1:
write(stdout, dbgBP[i].name & ": " & $abs(dbgBP[i].low) & ".." &
$abs(dbgBP[i].high) & dbgBP[i].filename)
write(stdout, dbgBP[i].name.data)
write(stdout, ": ")
write(stdout, abs(dbgBP[i].low))
write(stdout, "..")
write(stdout, abs(dbgBP[i].high))
write(stdout, dbgBP[i].filename)
if dbgBP[i].low < 0:
write(stdout, " [disabled]\n")
else:
write(stdout, "\n")
write(stdout, "***\n")
proc openAppend(filename: string): TFile =
if open(result, filename, fmAppend):
proc openAppend(filename: cstring): TFile =
var p: pointer = fopen(filename, "ab")
if p != nil:
result = cast[TFile](p)
write(result, "----------------------------------------\n")
proc dbgRepr(p: pointer, typ: PNimType): string =
@ -101,13 +137,17 @@ proc writeVariable(stream: TFile, slot: TVarSlot) =
writeln(stream, dbgRepr(slot.address, slot.typ))
proc ListFrame(stream: TFile, f: PExtendedFrame) =
write(stream, "*** endb| Frame (" & $f.f.len & " slots):\n")
write(stream, "*** endb| Frame (")
write(stream, f.f.len)
write(stream, " slots):\n")
for i in 0 .. f.f.len-1:
writeVariable(stream, f.slots[i])
write(stream, "***\n")
proc ListVariables(stream: TFile, f: PExtendedFrame) =
write(stream, "*** endb| Frame (" & $f.f.len & " slots):\n")
write(stream, "*** endb| Frame (")
write(stream, f.f.len)
write(stream, " slots):\n")
for i in 0 .. f.f.len-1:
writeln(stream, f.slots[i].name)
write(stream, "***\n")
@ -135,14 +175,19 @@ proc dbgShowCurrentProc(dbgFramePointer: PFrame) =
write(stdout, dbgFramePointer.procname)
write(stdout, " ***\n")
else:
write(stdout, "*** endb| (procedure name not available) ***\n")
write(stdout, "*** endb| (proc name not available) ***\n")
proc dbgShowExecutionPoint() =
write(stdout, "*** endb| " & $framePtr.filename &
"(" & $framePtr.line & ") " & $framePtr.procname & " ***\n")
write(stdout, "*** endb| ")
write(stdout, framePtr.filename)
write(stdout, "(")
write(stdout, framePtr.line)
write(stdout, ") ")
write(stdout, framePtr.procname)
write(stdout, " ***\n")
when defined(windows) or defined(dos) or defined(os2):
{.define: FileSystemCaseInsensitive.}
const
FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2)
proc fileMatches(c, bp: cstring): bool =
# bp = breakpoint filename
@ -162,7 +207,7 @@ proc fileMatches(c, bp: cstring): bool =
var x, y: char
x = bp[i]
y = c[i+clen-blen]
when defined(FileSystemCaseInsensitive):
when FileSystemCaseInsensitive:
if x >= 'A' and x <= 'Z': x = chr(ord(x) - ord('A') + ord('a'))
if y >= 'A' and y <= 'Z': y = chr(ord(y) - ord('A') + ord('a'))
if x != y: return false
@ -175,7 +220,7 @@ proc dbgBreakpointReached(line: int): int =
fileMatches(framePtr.filename, dbgBP[i].filename): return i
return -1
proc scanAndAppendWord(src: string, a: var string, start: int): int =
proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
result = start
# skip whitespace:
while src[result] in {'\t', ' '}: inc(result)
@ -187,20 +232,20 @@ proc scanAndAppendWord(src: string, a: var string, start: int): int =
else: break
inc(result)
proc scanWord(src: string, a: var string, start: int): int =
a = ""
proc scanWord(src: cstring, a: var TStaticStr, start: int): int =
setlen(a)
result = scanAndAppendWord(src, a, start)
proc scanFilename(src: string, a: var string, start: int): int =
proc scanFilename(src: cstring, a: var TStaticStr, start: int): int =
result = start
a = ""
setLen a
# skip whitespace:
while src[result] in {'\t', ' '}: inc(result)
while src[result] notin {'\t', ' ', '\0'}:
add(a, src[result])
inc(result)
proc scanNumber(src: string, a: var int, start: int): int =
proc scanNumber(src: cstring, a: var int, start: int): int =
result = start
a = 0
while src[result] in {'\t', ' '}: inc(result)
@ -222,7 +267,7 @@ q, quit quit the debugger and the program
s, step single step, stepping into routine calls
n, next single step, without stepping into routine calls
f, skipcurrent continue execution until the current routine finishes
c, continue continue execution until the next breakpoint
c, continue, r, run continue execution until the next breakpoint
i, ignore continue execution, ignore all breakpoints
BREAKPOINTS
b, break <name> [fromline [toline]] [file]
@ -247,14 +292,15 @@ maxdisplay <integer> set the display's recursion maximum
proc InvalidCommand() =
debugOut("[Warning] invalid command ignored (type 'h' for help) ")
proc hasExt(s: string): bool =
proc hasExt(s: cstring): bool =
# returns true if s has a filename extension
for i in countdown(len(s)-1, 0):
var i = 0
while s[i] != '\0':
if s[i] == '.': return true
return false
inc i
proc setBreakPoint(s: string, start: int) =
var dbgTemp: string
proc setBreakPoint(s: cstring, start: int) =
var dbgTemp: TStaticStr
var i = scanWord(s, dbgTemp, start)
if i <= start:
InvalidCommand()
@ -273,19 +319,22 @@ proc setBreakPoint(s: string, start: int) =
if dbgBP[x].high == 0: # set to low:
dbgBP[x].high = dbgBP[x].low
i = scanFilename(s, dbgTemp, i)
if not (dbgTemp.len == 0):
if not hasExt(dbgTemp): add(dbgTemp, ".nim")
dbgBP[x].filename = dbgTemp
if dbgTemp.len != 0:
debugOut("[Warning] explicit filename for breakpoint not supported")
when false:
if not hasExt(dbgTemp.data): add(dbgTemp, ".nim")
dbgBP[x].filename = dbgTemp
dbgBP[x].filename = framePtr.filename
else: # use current filename
dbgBP[x].filename = $framePtr.filename
dbgBP[x].filename = framePtr.filename
# skip whitespace:
while s[i] in {' ', '\t'}: inc(i)
if s[i] != '\0':
dec(dbgBPLen) # remove buggy breakpoint
InvalidCommand()
proc BreakpointSetEnabled(s: string, start, enabled: int) =
var dbgTemp: string
proc BreakpointSetEnabled(s: cstring, start, enabled: int) =
var dbgTemp: TStaticStr
var i = scanWord(s, dbgTemp, start)
if i <= start:
InvalidCommand()
@ -296,9 +345,9 @@ proc BreakpointSetEnabled(s: string, start, enabled: int) =
dbgBP[x].low = -dbgBP[x].low
dbgBP[x].high = -dbgBP[x].high
proc dbgEvaluate(stream: TFile, s: string, start: int,
proc dbgEvaluate(stream: TFile, s: cstring, start: int,
currFrame: PExtendedFrame) =
var dbgTemp: string
var dbgTemp: tstaticstr
var i = scanWord(s, dbgTemp, start)
while s[i] in {' ', '\t'}: inc(i)
var f = currFrame
@ -311,92 +360,108 @@ proc dbgEvaluate(stream: TFile, s: string, start: int,
if s[i] != '\0':
debugOut("[Warning] could not parse expr ")
return
var j = findVariable(f, dbgTemp)
var j = findVariable(f, dbgTemp.data)
if j < 0:
debugOut("[Warning] could not find variable ")
return
writeVariable(stream, f.slots[j])
proc dbgOut(s: string, start: int, currFrame: PExtendedFrame) =
var dbgTemp: string
proc dbgOut(s: cstring, start: int, currFrame: PExtendedFrame) =
var dbgTemp: tstaticstr
var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0:
InvalidCommand()
return
var stream = openAppend(dbgTemp)
var stream = openAppend(dbgTemp.data)
if stream == nil:
debugOut("[Warning] could not open or create file ")
return
dbgEvaluate(stream, s, i, currFrame)
close(stream)
proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) =
var dbgTemp: string
proc dbgStackFrame(s: cstring, start: int, currFrame: PExtendedFrame) =
var dbgTemp: TStaticStr
var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0:
# just write it to stdout:
ListFrame(stdout, currFrame)
else:
var stream = openAppend(dbgTemp)
var stream = openAppend(dbgTemp.data)
if stream == nil:
debugOut("[Warning] could not open or create file ")
return
ListFrame(stream, currFrame)
close(stream)
proc readLine(f: TFile, line: var TStaticStr): bool =
while True:
var c = fgetc(f)
if c < 0'i32:
if line.len > 0: break
else: return false
if c == 10'i32: break # LF
if c == 13'i32: # CR
c = fgetc(f) # is the next char LF?
if c != 10'i32: ungetc(c, f) # no, put the character back
break
add line, chr(int(c))
result = true
proc dbgWriteStackTrace(f: PFrame)
proc CommandPrompt() =
# if we return from this routine, user code executes again
var
again = True
dbgFramePtr = framePtr # for going down and up the stack
dbgDown = 0 # how often we did go down
dbgTemp: TStaticStr
while again:
write(stdout, "*** endb| >>")
var tmp = readLine(stdin)
if tmp.len > 0: dbgUser = tmp
let oldLen = dbgUser.len
dbgUser.len = 0
if not readLine(stdin, dbgUser): break
if dbgUser.len == 0: dbgUser.len = oldLen
# now look what we have to do:
var dbgTemp: string
var i = scanWord(dbgUser, dbgTemp, 0)
case dbgTemp
of "": InvalidCommand()
of "s", "step":
var i = scanWord(dbgUser.data, dbgTemp, 0)
template `?`(x: expr): expr = dbgTemp == cstring(x)
if ?"s" or ?"step":
dbgState = dbStepInto
again = false
of "n", "next":
elif ?"n" or ?"next":
dbgState = dbStepOver
dbgSkipToFrame = framePtr
again = false
of "f", "skipcurrent":
elif ?"f" or ?"skipcurrent":
dbgState = dbSkipCurrent
dbgSkipToFrame = framePtr.prev
again = false
of "c", "continue":
elif ?"c" or ?"continue" or ?"r" or ?"run":
dbgState = dbBreakpoints
again = false
of "i", "ignore":
elif ?"i" or ?"ignore":
dbgState = dbOff
again = false
of "h", "help":
elif ?"h" or ?"help":
dbgHelp()
of "q", "quit":
elif ?"q" or ?"quit":
dbgState = dbQuiting
dbgAborting = True
again = false
quit(1) # BUGFIX: quit with error code > 0
of "e", "eval":
dbgEvaluate(stdout, dbgUser, i, cast[PExtendedFrame](dbgFramePtr))
of "o", "out":
dbgOut(dbgUser, i, cast[PExtendedFrame](dbgFramePtr))
of "stackframe":
dbgStackFrame(dbgUser, i, cast[PExtendedFrame](dbgFramePtr))
of "w", "where":
elif ?"e" or ?"eval":
dbgEvaluate(stdout, dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
elif ?"o" or ?"out":
dbgOut(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
elif ?"stackframe":
dbgStackFrame(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr))
elif ?"w" or ?"where":
dbgShowExecutionPoint()
of "l", "locals":
elif ?"l" or ?"locals":
ListVariables(stdout, cast[PExtendedFrame](dbgFramePtr))
of "g", "globals":
elif ?"g" or ?"globals":
ListVariables(stdout, addr(dbgGlobalData))
of "u", "up":
elif ?"u" or ?"up":
if dbgDown <= 0:
debugOut("[Warning] cannot go up any further ")
else:
@ -405,33 +470,32 @@ proc CommandPrompt() =
dbgFramePtr = dbgFramePtr.prev
dec(dbgDown)
dbgShowCurrentProc(dbgFramePtr)
of "d", "down":
elif ?"d" or ?"down":
if dbgFramePtr != nil:
inc(dbgDown)
dbgFramePtr = dbgFramePtr.prev
dbgShowCurrentProc(dbgFramePtr)
else:
debugOut("[Warning] cannot go down any further ")
of "bt", "backtrace":
WriteStackTrace()
of "b", "break":
setBreakPoint(dbgUser, i)
of "breakpoints":
elif ?"bt" or ?"backtrace":
dbgWriteStackTrace(framePtr)
elif ?"b" or ?"break":
setBreakPoint(dbgUser.data, i)
elif ?"breakpoints":
ListBreakPoints()
of "disable":
BreakpointSetEnabled(dbgUser, i, -1)
of "enable":
BreakpointSetEnabled(dbgUser, i, +1)
of "maxdisplay":
elif ?"disable":
BreakpointSetEnabled(dbgUser.data, i, -1)
elif ?"enable":
BreakpointSetEnabled(dbgUser.data, i, +1)
elif ?"maxdisplay":
var parsed: int
i = scanNumber(dbgUser, parsed, i)
if dbgUser[i-1] in {'0'..'9'}:
i = scanNumber(dbgUser.data, parsed, i)
if dbgUser.data[i-1] in {'0'..'9'}:
if parsed == 0: maxDisplayRecDepth = -1
else: maxDisplayRecDepth = parsed
else:
InvalidCommand()
else:
InvalidCommand()
else: InvalidCommand()
proc endbStep() =
# we get into here if an unhandled exception has been raised
@ -441,10 +505,10 @@ proc endbStep() =
CommandPrompt()
proc checkForBreakpoint() =
var i = dbgBreakpointReached(framePtr.line)
let i = dbgBreakpointReached(framePtr.line)
if i >= 0:
write(stdout, "*** endb| reached ")
write(stdout, dbgBP[i].name)
write(stdout, dbgBP[i].name.data)
write(stdout, " in ")
write(stdout, framePtr.filename)
write(stdout, "(")
@ -458,16 +522,19 @@ proc checkForBreakpoint() =
proc dbgRegisterBreakpoint(line: int,
filename, name: cstring) {.compilerproc.} =
var x = dbgBPlen
let x = dbgBPlen
if x >= high(dbgBP):
debugOut("[Warning] cannot register breakpoint")
return
inc(dbgBPlen)
dbgBP[x].name = $name
dbgBP[x].filename = $filename
dbgBP[x].name.assign(name)
dbgBP[x].filename = filename
dbgBP[x].low = line
dbgBP[x].high = line
proc dbgRegisterGlobal(name: cstring, address: pointer,
typ: PNimType) {.compilerproc.} =
var i = dbgGlobalData.f.len
let i = dbgGlobalData.f.len
if i >= high(dbgGlobalData.slots):
debugOut("[Warning] cannot register global ")
return
@ -476,14 +543,179 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
dbgGlobalData.slots[i].address = address
inc(dbgGlobalData.f.len)
type
THash = int
TWatchpoint {.pure, final.} = object
name: cstring
address: pointer
typ: PNimType
oldValue: THash
var
Watchpoints: array [0..99, TWatchpoint]
WatchpointsLen: int
proc `!&`(h: THash, val: int): THash {.inline.} =
result = h +% val
result = result +% result shl 10
result = result xor (result shr 6)
proc `!$`(h: THash): THash {.inline.} =
result = h +% h shl 3
result = result xor (result shr 11)
result = result +% result shl 15
proc hash(Data: Pointer, Size: int): THash =
var h: THash = 0
var p = cast[cstring](Data)
var i = 0
var s = size
while s > 0:
h = h !& ord(p[i])
Inc(i)
Dec(s)
result = !$h
proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
h: THash): THash
proc genericHashAux(dest: Pointer, n: ptr TNimNode, shallow: bool,
h: THash): THash =
var d = cast[TAddress](dest)
case n.kind
of nkSlot:
result = genericHashAux(cast[pointer](d +% n.offset), n.typ, shallow, h)
of nkList:
result = h
for i in 0..n.len-1:
result = result !& genericHashAux(dest, n.sons[i], shallow, result)
of nkCase:
result = h !& hash(cast[pointer](d +% n.offset), n.typ.size)
var m = selectBranch(dest, n)
if m != nil: result = genericHashAux(dest, m, shallow, result)
of nkNone: sysAssert(false, "genericHashAux")
proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool,
h: THash): THash =
sysAssert(mt != nil, "genericHashAux 2")
case mt.Kind
of tyString:
var x = cast[ppointer](dest)[]
result = h
if x != nil:
let s = cast[NimString](x)
let y = cast[pointer](cast[int](x) -% 2*sizeof(int))
result = result !& hash(x, s.len + 2*sizeof(int))
of tySequence:
var x = cast[ppointer](dest)
var dst = cast[taddress](cast[ppointer](dest)[])
result = h
if dst != 0:
for i in 0..cast[pgenericseq](dst).len-1:
result = result !& genericHashAux(
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
mt.Base, shallow, result)
of tyObject, tyTuple:
# we don't need to copy m_type field for tyObject, as they are equal anyway
result = genericHashAux(dest, mt.node, shallow, h)
of tyArray, tyArrayConstr:
let d = cast[TAddress](dest)
result = h
for i in 0..(mt.size div mt.base.size)-1:
result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size),
mt.base, shallow, result)
of tyRef:
if shallow:
result = h !& hash(dest, mt.size)
else:
var s = cast[ppointer](dest)[]
if s != nil: result = genericHashAux(s, mt.base, shallow, h)
else:
result = h !& hash(dest, mt.size) # hash raw bits
proc genericHash(dest: Pointer, mt: PNimType): int =
result = genericHashAux(dest, mt, false, 0)
proc dbgRegisterWatchpoint(address: pointer, name: cstring,
typ: PNimType) {.compilerproc.} =
let L = WatchpointsLen
for i in 0.. <L:
if Watchpoints[i].name == name:
# address may have changed:
Watchpoints[i].address = address
return
if L >= watchPoints.high:
debugOut("[Warning] cannot register watchpoint")
return
Watchpoints[L].name = name
Watchpoints[L].address = address
Watchpoints[L].typ = typ
Watchpoints[L].oldValue = genericHash(address, typ)
inc WatchpointsLen
proc dbgWriteStackTrace(f: PFrame) =
const
firstCalls = 32
var
it = f
i = 0
total = 0
tempFrames: array [0..127, PFrame]
while it != nil and i <= high(tempFrames)-(firstCalls-1):
# the (-1) is for a nil entry that marks where the '...' should occur
tempFrames[i] = it
inc(i)
inc(total)
it = it.prev
var b = it
while it != nil:
inc(total)
it = it.prev
for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev
if total != i:
tempFrames[i] = nil
inc(i)
while b != nil and i <= high(tempFrames):
tempFrames[i] = b
inc(i)
b = b.prev
for j in countdown(i-1, 0):
if tempFrames[j] == nil:
write(stdout, "(")
write(stdout, (total-i-1))
write(stdout, " calls omitted) ...")
else:
write(stdout, tempFrames[j].filename)
if tempFrames[j].line > 0:
write(stdout, '(')
write(stdout, tempFrames[j].line)
write(stdout, ')')
write(stdout, ' ')
write(stdout, tempFrames[j].procname)
write(stdout, "\n")
proc checkWatchpoints =
let L = WatchpointsLen
for i in 0.. <L:
let newHash = genericHash(Watchpoints[i].address, Watchpoints[i].typ)
if newHash != Watchpoints[i].oldValue:
dbgWriteStackTrace(framePtr)
debugOut(Watchpoints[i].name)
Watchpoints[i].oldValue = newHash
proc endb(line: int) {.compilerproc.} =
# This proc is called before every Nimrod code line!
# Thus, it must have as few parameters as possible to keep the
# code size small!
# Check if we are at an enabled breakpoint or "in the mood"
if framePtr == nil: return
let oldState = dbgState
#dbgState = dbOff
#if oldState != dbOff:
checkWatchpoints()
framePtr.line = line # this is done here for smaller code size!
if dbgLineHook != nil: dbgLineHook()
case dbgState
case oldState
of dbStepInto:
# we really want the command prompt here:
dbgShowExecutionPoint()
@ -497,3 +729,9 @@ proc endb(line: int) {.compilerproc.} =
of dbBreakpoints: # debugger is only interested in breakpoints
checkForBreakpoint()
else: nil
proc initDebugger {.inline.} =
dbgState = dbStepInto
dbgUser.len = 1
dbgUser.data[0] = 's'

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 nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
proc internalAssert(file: cstring, line: int) {.noStackFrame, compilerproc.} =
var
e: ref EAssertionFailed
new(e)
asm """`e`.message = "[Assertion failure] file: "+`file`+", line: "+`line`"""
raise e
include "system/hti"
proc isFatPointer(ti: PNimType): bool =

View file

@ -238,18 +238,6 @@ proc reraiseException() {.compilerRtl.} =
else:
raiseException(currException, currException.name)
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
if not cond:
var gAssertionFailed: ref EAssertionFailed
new(gAssertionFailed)
gAssertionFailed.msg = newStringOfCap(200)
add(gAssertionFailed.msg, "[Assertion failure] file: ")
add(gAssertionFailed.msg, file)
add(gAssertionFailed.msg, " line: ")
add(gAssertionFailed.msg, $line)
add(gAssertionFailed.msg, "\n")
raise gAssertionFailed
proc WriteStackTrace() =
when hasSomeStackTrace:
var s = ""

View file

@ -111,7 +111,7 @@ when debugGC:
proc writeCell(msg: CString, c: PCell) =
var kind = -1
if c.typ != nil: kind = ord(c.typ.kind)
when debugGC:
when leakDetector:
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n",
msg, c, kind, c.refcount shr rcShift, c.filename, c.line)
else:
@ -277,6 +277,10 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
# the test for '!= nil' is correct, but I got tired of the segfaults
# resulting from the crappy stack checking:
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
else:
# can't be an interior pointer if it's a stack location!
sysAssert(interiorAllocatedPtr(gch.region, dest)==nil,
"stack loc AND interior pointer")
dest[] = src
proc initGC() =
@ -400,7 +404,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT
res.typ = typ
when debugGC and not hasThreadSupport:
when leakDetector and not hasThreadSupport:
if framePtr != nil and framePtr.prev != nil:
res.filename = framePtr.prev.filename
res.line = framePtr.prev.line
@ -437,7 +441,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT
res.typ = typ
when debugGC and not hasThreadSupport:
when leakDetector and not hasThreadSupport:
if framePtr != nil and framePtr.prev != nil:
res.filename = framePtr.prev.filename
res.line = framePtr.prev.line
@ -510,6 +514,9 @@ proc doOperation(p: pointer, op: TWalkOp) =
sysAssert(c != nil, "doOperation: 1")
case op # faster than function pointers because of easy prediction
of waZctDecRef:
#if not isAllocatedPtr(gch.region, c):
# return
# c_fprintf(c_stdout, "[GC] decref bug: %p", c)
sysAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
sysAssert(c.refcount >=% rcIncrement, "doOperation 2")
c.refcount = c.refcount -% rcIncrement
@ -776,7 +783,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
proc collectCT(gch: var TGcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region) >= gch.cycleThreshold) or stressGC) and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0:
sysAssert(allocInv(gch.region), "collectCT: begin")
@ -789,7 +796,7 @@ proc collectCT(gch: var TGcHeap) =
inc(gch.stat.stackScans)
collectZCT(gch)
when cycleGC:
if getOccupiedMem(gch.region) >= gch.cycleThreshold or stressGC:
if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
collectCycles(gch)
collectZCT(gch)
inc(gch.stat.cycleCollections)

View file

@ -17,13 +17,16 @@ const
debugGC = false # we wish to debug the GC...
logGC = false
traceGC = false # extensive debugging
reallyDealloc = true # for debugging purposes this can be set to false
alwaysCycleGC = false
alwaysGC = false # collect after every memory allocation (for debugging)
leakDetector = false
overwriteFree = false
cycleGC = true # (de)activate the cycle GC
stressGC = false
reallyDealloc = true # for debugging purposes this can be set to false
reallyOsDealloc = true
coalescRight = true
coalescLeft = true
overwriteFree = false
type
PPointer = ptr pointer
@ -229,7 +232,8 @@ else:
include "system/alloc"
include "system/cellsets"
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell")
when not leakDetector:
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell")
include "system/gc"
{.pop.}

View file

@ -125,7 +125,7 @@ proc readAll(file: TFile): TaintedString =
# don't know the overall length of the TFile.
var len = rawFileSize(file)
if len >= 0:
result = readAllFile(file, len).TaintedSTring
result = readAllFile(file, len).TaintedString
else:
result = readAllBuffer(file).TaintedString