Rename cb0 to asyncProcName_continue + other improvements to async tracebacks.
This commit is contained in:
parent
a9a13e470b
commit
3a790c9c72
3 changed files with 70 additions and 41 deletions
|
|
@ -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:")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
exitcode: 0
|
exitcode: 0
|
||||||
|
disabled: "windows"
|
||||||
output: '''
|
output: '''
|
||||||
b failure
|
b failure
|
||||||
Async traceback:
|
Async traceback:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue