Skip to content

Commit

Permalink
Nice tracktraces proof of concept
Browse files Browse the repository at this point in the history
  • Loading branch information
yglukhov committed Aug 16, 2019
1 parent 693f741 commit 490db83
Showing 1 changed file with 57 additions and 14 deletions.
71 changes: 57 additions & 14 deletions chronos/asyncfutures2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type

FutureBase* = ref object of RootObj ## Untyped future.
location: array[2, ptr SrcLoc]
filename: cstring
lineNo: int
procName: cstring
callbacks: Deque[AsyncCallback]
cancelcb*: CallbackFunc
child*: FutureBase
Expand Down Expand Up @@ -69,6 +72,9 @@ type
var currentID* {.threadvar.}: int
currentID = 0

proc callerLocation(): PFrame =
getFrame()

# ZAH: This seems unnecessary. Isn't it easy to introduce a seperate
# module for the dispatcher type, so it can be directly referenced here?
var callSoonHolder {.threadvar.}: CallSoonProc
Expand All @@ -85,61 +91,64 @@ proc callSoon*(c: CallbackFunc, u: pointer = nil) =
## Call ``cbproc`` "soon".
callSoonHolder(c, u)

template setupFutureBase(loc: ptr SrcLoc) =
template setupFutureBase(loc: ptr SrcLoc, caller: PFrame) =
new(result)
result.state = FutureState.Pending
result.stackTrace = getStackTrace()
result.id = currentID
result.location[LocCreateIndex] = loc
result.filename = caller.prev.filename
result.lineno = caller.prev.line
result.procName = caller.prev.procname
currentID.inc()

## ZAH: As far as I undestand `fromProc` is just a debugging helper.
## It would be more efficient if it's represented as a simple statically
## known `char *` in the final program (so it needs to be a `cstring` in Nim).
## The public API can be defined as a template expecting a `static[string]`
## and converting this immediately to a `cstring`.
proc newFuture[T](loc: ptr SrcLoc): Future[T] =
setupFutureBase(loc)
proc newFuture[T](loc: ptr SrcLoc, srcLoc: PFrame): Future[T] =
setupFutureBase(loc, srcLoc)

proc newFutureSeq[A, B](loc: ptr SrcLoc): FutureSeq[A, B] =
setupFutureBase(loc)
proc newFutureSeq[A, B](loc: ptr SrcLoc, srcLoc: PFrame): FutureSeq[A, B] =
setupFutureBase(loc, srcLoc)

proc newFutureStr[T](loc: ptr SrcLoc): FutureStr[T] =
setupFutureBase(loc)
proc newFutureStr[T](loc: ptr SrcLoc, srcLoc: PFrame): FutureStr[T] =
setupFutureBase(loc, srcLoc)

proc newFutureVar[T](loc: ptr SrcLoc): FutureVar[T] =
FutureVar[T](newFuture[T](loc))
proc newFutureVar[T](loc: ptr SrcLoc, srcLoc: PFrame): FutureVar[T] =
FutureVar[T](newFuture[T](loc, srcLoc))

template newFuture*[T](fromProc: static[string] = ""): auto =
## Creates a new future.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
newFuture[T](getSrcLocation(fromProc))
newFuture[T](getSrcLocation(fromProc), callerLocation())

template newFutureSeq*[A, B](fromProc: static[string] = ""): auto =
## Create a new future which can hold/preserve GC sequence until future will
## not be completed.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
newFutureSeq[A, B](getSrcLocation(fromProc))
newFutureSeq[A, B](getSrcLocation(fromProc), callerLocation())

template newFutureStr*[T](fromProc: static[string] = ""): auto =
## Create a new future which can hold/preserve GC string until future will
## not be completed.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
newFutureStr[T](getSrcLocation(fromProc))
newFutureStr[T](getSrcLocation(fromProc), callerLocation())

template newFutureVar*[T](fromProc: static[string] = ""): auto =
## Create a new ``FutureVar``. This Future type is ideally suited for
## situations where you want to avoid unnecessary allocations of Futures.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
newFutureVar[T](getSrcLocation(fromProc))
newFutureVar[T](getSrcLocation(fromProc), callerLocation())

proc clean*[T](future: FutureVar[T]) =
## Resets the ``finished`` status of ``future``.
Expand Down Expand Up @@ -395,6 +404,40 @@ proc `$`*(entries: seq[StackTraceEntry]): string =
if hint.len > 0:
result.add(spaces(indent+2) & "## " & hint & "\n")

proc getStackTraceEntries(fut: FutureBase): string =
var f = fut

result = "---- Nice stacktrace ------\n"

var topFut = fut

while not f.isNil:
topFut = f
result &= $f.filename & " (" & $f.lineno & ") " & $f.procName & " ---\n"
f = f.child

var stStart = 0

let s = fut.error.getStackTraceEntries()
var i = 1
for e in s:
if e.line == -10: break
if e.procName == "poll":
stStart = i
elif e.filename == topFut.filename and e.line == topFut.lineno:
stStart = i
break
inc i

for i in stStart ..< s.len:
if s[i].line == -10: break
result &= $s[i].filename & " (" & $s[i].line & ") " & $s[i].procname & "\n"

result &= "---- End of nice stacktrace ------\n"
result &= "---- Raw upper exception stacktrace ------\n"
result &= $fut.error.getStackTraceEntries()
result &= "stStart: " & $stStart & "\n"

proc injectStacktrace(future: FutureBase) =
const header = "\nAsync traceback:\n"

Expand All @@ -407,7 +450,7 @@ proc injectStacktrace(future: FutureBase) =

var newMsg = exceptionMsg & header

let entries = getStackTraceEntries(future.error)
let entries = getStackTraceEntries(future)
newMsg.add($entries)

newMsg.add("Exception message: " & exceptionMsg & "\n")
Expand Down

0 comments on commit 490db83

Please sign in to comment.