Rename cb0 to asyncProcName_continue + other improvements to async tracebacks.

This commit is contained in:
Dominik Picheta 2017-11-26 13:45:58 +00:00 • committed by Andreas Rumpf
commit 3a790c9c72
3 changed files with 70 additions and 41 deletions

View file

@ -231,13 +231,9 @@ proc diff[T](a, b: seq[T]): (int, seq[T]) =
break break
return (lastIndex, b[lastIndex .. ^1]) return (lastIndex, b[lastIndex .. ^1])
proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] = proc mergeEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
proc get(entries: seq[StackTraceEntry], i: int): StackTraceEntry = ## Merges stack trace entries containing re-raise entries into one
if i >= entries.len: ## continuous stack trace.
return StackTraceEntry(procName: "", line: 0, filename: "")
else:
return entries[i]
result = @[] result = @[]
var i = 0 var i = 0
while i < entries.len: while i < entries.len:
@ -252,7 +248,7 @@ proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
reRaiseEnd.inc() reRaiseEnd.inc()
assert entries[reRaiseEnd].procName.isNil assert entries[reRaiseEnd].procName.isNil
assert entries[reRaiseEnd].line == -100 # Signifies end of re-raise block. assert entries[reRaiseEnd].line == -100 # Signifies end of re-raise block.
let reRaisedEntries = processEntries(entries[i+1 .. reRaiseEnd-1]) let reRaisedEntries = mergeEntries(entries[i+1 .. reRaiseEnd-1])
let (lastIndex, remainder) = diff(result, reRaisedEntries) let (lastIndex, remainder) = diff(result, reRaisedEntries)
for i in 0..<remainder.len: for i in 0..<remainder.len:
@ -260,16 +256,34 @@ proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
i = reRaiseEnd+1 i = reRaiseEnd+1
continue continue
result.add(entry)
i.inc
proc shortenEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
## Analyzes the entries for patterns and processes them.
proc get(entries: seq[StackTraceEntry], i: int): StackTraceEntry =
if i >= entries.len:
return StackTraceEntry(procName: "", line: 0, filename: "")
else:
return entries[i]
result = @[]
var i = 0
while i < entries.len:
var entry = entries[i]
# Detect this pattern: # Detect this pattern:
# (procname: a, line: 393, filename: asyncmacro.nim) # (procname: a, line: 393, filename: asyncmacro.nim)
# (procname: cb0, line: 34, filename: asyncmacro.nim) # (procname: a_continue, line: 34, filename: asyncmacro.nim)
# (procname: aIter, line: 40, filename: tasync_traceback.nim) # (procname: aIter, line: 40, filename: tasync_traceback.nim)
let second = get(entries, i+1) let second = get(entries, i+1)
let third = get(entries, i+2) let third = get(entries, i+2)
let fitsPattern = let fitsPattern =
cmpIgnoreStyle($entry.filename, "asyncmacro.nim") == 0 and cmpIgnoreStyle($entry.filename, "asyncmacro.nim") == 0 and
cmpIgnoreStyle($second.filename, "asyncmacro.nim") == 0 and cmpIgnoreStyle($second.filename, "asyncmacro.nim") == 0 and
cmpIgnoreStyle($second.procName, "cb0") == 0 and ($second.procName).startsWith($entry.procName) and
($second.procName).endsWith("continue") and
cmpIgnoreStyle($third.procName, $entry.procName & "iter") == 0 cmpIgnoreStyle($third.procName, $entry.procName & "iter") == 0
if fitsPattern: if fitsPattern:
@ -287,10 +301,8 @@ proc getHint(entry: StackTraceEntry): string =
## We try to provide some hints about stack trace entries that the user ## We try to provide some hints about stack trace entries that the user
## may not be familiar with, in particular calls inside the stdlib. ## may not be familiar with, in particular calls inside the stdlib.
result = "" result = ""
case ($entry.procName).normalize() let name = ($entry.procName).normalize()
of "cb0": case name
if cmpIgnoreStyle($entry.filename, "asyncmacro.nim") == 0:
return "Resumes an async procedure"
of "processpendingcallbacks": of "processpendingcallbacks":
if cmpIgnoreStyle($entry.filename, "asyncdispatch.nim") == 0: if cmpIgnoreStyle($entry.filename, "asyncdispatch.nim") == 0:
return "Executes pending callbacks" return "Executes pending callbacks"
@ -298,6 +310,35 @@ proc getHint(entry: StackTraceEntry): string =
if cmpIgnoreStyle($entry.filename, "asyncdispatch.nim") == 0: if cmpIgnoreStyle($entry.filename, "asyncdispatch.nim") == 0:
return "Processes asynchronous completion events" return "Processes asynchronous completion events"
if name.endsWith("continue"):
if cmpIgnoreStyle($entry.filename, "asyncmacro.nim") == 0:
return "Resumes an async procedure"
proc `$`*(entries: seq[StackTraceEntry]): string =
result = ""
# Find longest filename & line number combo for alignment purposes.
var longestLeft = 0
for entry in entries:
let left = $entry.filename & $entry.line
if left.len > longestLeft:
longestLeft = left.len
const indent = 2
# Format the entries.
for entry in entries:
let indentStr = spaces(indent)
let left = "$#($#)" % [$entry.filename, $entry.line]
result.add("$#$#$# $#\n" % [
indentStr,
left,
spaces(longestLeft - left.len + 2),
$entry.procName
])
let hint = getHint(entry)
if hint.len > 0:
result.add(indentStr & "└─" & hint & "\n")
proc injectStacktrace[T](future: Future[T]) = proc injectStacktrace[T](future: Future[T]) =
when not defined(release): when not defined(release):
const header = "\nAsync traceback:\n" const header = "\nAsync traceback:\n"
@ -309,29 +350,15 @@ proc injectStacktrace[T](future: Future[T]) =
let start = exceptionMsg.find(header) let start = exceptionMsg.find(header)
exceptionMsg = exceptionMsg[0..<start] exceptionMsg = exceptionMsg[0..<start]
let entries = getStackTraceEntries(future.error).processEntries()
var newMsg = exceptionMsg & header var newMsg = exceptionMsg & header
# Find longest filename & line number combo for alignment purposes. let entries = getStackTraceEntries(future.error).mergeEntries()
var longestLeft = 0 let shortEntries = entries.shortenEntries()
for entry in entries: newMsg.add($shortEntries)
let left = $entry.filename & $entry.line if entries.len > shortEntries.len:
if left.len > longestLeft: newMsg.add("\nDetailed Async traceback:\n")
longestLeft = left.len newMsg.add($entries)
const indent = " "
# Format the entries.
for entry in entries:
let left = "$#($#)" % [$entry.filename, $entry.line]
newMsg.add("$#$#$# $#\n" % [
indent,
left,
spaces(longestLeft - left.len + 2),
$entry.procName
])
let hint = getHint(entry)
if hint.len > 0:
newMsg.add(indent & "└─" & hint & "\n")
newMsg.add("Exception message: " & exceptionMsg & "\n") newMsg.add("Exception message: " & exceptionMsg & "\n")
newMsg.add("Exception type:") newMsg.add("Exception type:")

View file

@ -25,10 +25,10 @@ proc skipStmtList(node: NimNode): NimNode {.compileTime.} =
result = node[0] result = node[0]
template createCb(retFutureSym, iteratorNameSym, template createCb(retFutureSym, iteratorNameSym,
name, futureVarCompletions: untyped) = strName, identName, futureVarCompletions: untyped) =
var nameIterVar = iteratorNameSym var nameIterVar = iteratorNameSym
#{.push stackTrace: off.} #{.push stackTrace: off.}
proc cb0 {.closure.} = proc identName {.closure.} =
try: try:
if not nameIterVar.finished: if not nameIterVar.finished:
var next = nameIterVar() var next = nameIterVar()
@ -36,11 +36,11 @@ template createCb(retFutureSym, iteratorNameSym,
if not retFutureSym.finished: if not retFutureSym.finished:
let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " & let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " &
"`nil` Future?" "`nil` Future?"
raise newException(AssertionError, msg % name) raise newException(AssertionError, msg % strName)
else: else:
{.gcsafe.}: {.gcsafe.}:
{.push hint[ConvFromXtoItselfNotNeeded]: off.} {.push hint[ConvFromXtoItselfNotNeeded]: off.}
next.callback = (proc() {.closure, gcsafe.})(cb0) next.callback = (proc() {.closure, gcsafe.})(identName)
{.pop.} {.pop.}
except: except:
futureVarCompletions futureVarCompletions
@ -52,7 +52,7 @@ template createCb(retFutureSym, iteratorNameSym,
else: else:
retFutureSym.fail(getCurrentException()) retFutureSym.fail(getCurrentException())
cb0() identName()
#{.pop.} #{.pop.}
proc generateExceptionCheck(futSym, proc generateExceptionCheck(futSym,
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} = tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
@ -389,9 +389,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
outerProcBody.add(closureIterator) outerProcBody.add(closureIterator)
# -> createCb(retFuture) # -> createCb(retFuture)
#var cbName = newIdentNode("cb") var cbName = genSym(nskProc, prcName & "_continue")
var procCb = getAst createCb(retFutureSym, iteratorNameSym, var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prcName), newStrLitNode(prcName),
cbName,
createFutureVarCompletions(futureVarIdents, nil)) createFutureVarCompletions(futureVarIdents, nil))
outerProcBody.add procCb outerProcBody.add procCb

View file

@ -1,5 +1,6 @@
discard """ discard """
exitcode: 0 exitcode: 0
disabled: "windows"
output: ''' output: '''
b failure b failure
Async traceback: Async traceback: