Go through the re-raise stacks for more detailed tracebacks.
This commit is contained in:
parent
9ca6afe73a
commit
a9a13e470b
2 changed files with 87 additions and 15 deletions
|
|
@ -217,6 +217,20 @@ proc `callback=`*[T](future: Future[T],
|
||||||
## If future has already completed then ``cb`` will be called immediately.
|
## If future has already completed then ``cb`` will be called immediately.
|
||||||
future.callback = proc () = cb(future)
|
future.callback = proc () = cb(future)
|
||||||
|
|
||||||
|
proc diff[T](a, b: seq[T]): (int, seq[T]) =
|
||||||
|
## Iterates through both sequences until the items do not match,
|
||||||
|
## returns the remainder of `b` after the last item that does not match
|
||||||
|
## together with the index of the last match.
|
||||||
|
##
|
||||||
|
## .. code-block::nim
|
||||||
|
## doAssert(diff(@[1,2,42,123], @[1,2,123,678,21]) == (1, @[123,678,21]))
|
||||||
|
var lastIndex = 0
|
||||||
|
for i in 0 .. <min(a.len, b.len):
|
||||||
|
lastIndex = i
|
||||||
|
if a[i] != b[i]:
|
||||||
|
break
|
||||||
|
return (lastIndex, b[lastIndex .. ^1])
|
||||||
|
|
||||||
proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
|
proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
|
||||||
proc get(entries: seq[StackTraceEntry], i: int): StackTraceEntry =
|
proc get(entries: seq[StackTraceEntry], i: int): StackTraceEntry =
|
||||||
if i >= entries.len:
|
if i >= entries.len:
|
||||||
|
|
@ -230,8 +244,21 @@ proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
|
||||||
var entry = entries[i]
|
var entry = entries[i]
|
||||||
|
|
||||||
if entry.procName.isNil:
|
if entry.procName.isNil:
|
||||||
# Start of a re-raise traceback which we do not care for.
|
# Start of a re-raise traceback which may contain more info.
|
||||||
break
|
# Find where the re-raised traceback ends.
|
||||||
|
assert entry.line == -10 # Signifies start of re-raise block.
|
||||||
|
var reRaiseEnd = i+1
|
||||||
|
while reRaiseEnd < entries.len and not entries[reRaiseEnd].procName.isNil:
|
||||||
|
reRaiseEnd.inc()
|
||||||
|
assert entries[reRaiseEnd].procName.isNil
|
||||||
|
assert entries[reRaiseEnd].line == -100 # Signifies end of re-raise block.
|
||||||
|
let reRaisedEntries = processEntries(entries[i+1 .. reRaiseEnd-1])
|
||||||
|
|
||||||
|
let (lastIndex, remainder) = diff(result, reRaisedEntries)
|
||||||
|
for i in 0..<remainder.len:
|
||||||
|
result.insert(remainder[i], lastIndex+i)
|
||||||
|
i = reRaiseEnd+1
|
||||||
|
continue
|
||||||
|
|
||||||
# Detect this pattern:
|
# Detect this pattern:
|
||||||
# (procname: a, line: 393, filename: asyncmacro.nim)
|
# (procname: a, line: 393, filename: asyncmacro.nim)
|
||||||
|
|
@ -273,14 +300,17 @@ proc getHint(entry: StackTraceEntry): string =
|
||||||
|
|
||||||
proc injectStacktrace[T](future: Future[T]) =
|
proc injectStacktrace[T](future: Future[T]) =
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
const header = "Async traceback:\n"
|
const header = "\nAsync traceback:\n"
|
||||||
|
|
||||||
let originalMsg = future.error.msg
|
var exceptionMsg = future.error.msg
|
||||||
if header in originalMsg:
|
if header in exceptionMsg:
|
||||||
return
|
# This is messy: extract the original exception message from the msg
|
||||||
|
# containing the async traceback.
|
||||||
|
let start = exceptionMsg.find(header)
|
||||||
|
exceptionMsg = exceptionMsg[0..<start]
|
||||||
|
|
||||||
let entries = getStackTraceEntries(future.error).processEntries()
|
let entries = getStackTraceEntries(future.error).processEntries()
|
||||||
future.error.msg = originalMsg & "\n" & header
|
var newMsg = exceptionMsg & header
|
||||||
|
|
||||||
# Find longest filename & line number combo for alignment purposes.
|
# Find longest filename & line number combo for alignment purposes.
|
||||||
var longestLeft = 0
|
var longestLeft = 0
|
||||||
|
|
@ -293,7 +323,7 @@ proc injectStacktrace[T](future: Future[T]) =
|
||||||
# Format the entries.
|
# Format the entries.
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
let left = "$#($#)" % [$entry.filename, $entry.line]
|
let left = "$#($#)" % [$entry.filename, $entry.line]
|
||||||
future.error.msg.add("$#$#$# $#\n" % [
|
newMsg.add("$#$#$# $#\n" % [
|
||||||
indent,
|
indent,
|
||||||
left,
|
left,
|
||||||
spaces(longestLeft - left.len + 2),
|
spaces(longestLeft - left.len + 2),
|
||||||
|
|
@ -301,14 +331,15 @@ proc injectStacktrace[T](future: Future[T]) =
|
||||||
])
|
])
|
||||||
let hint = getHint(entry)
|
let hint = getHint(entry)
|
||||||
if hint.len > 0:
|
if hint.len > 0:
|
||||||
future.error.msg.add(indent & "└─" & hint & "\n")
|
newMsg.add(indent & "└─" & hint & "\n")
|
||||||
|
|
||||||
future.error.msg.add("Exception message: " & originalMsg & "\n")
|
newMsg.add("Exception message: " & exceptionMsg & "\n")
|
||||||
future.error.msg.add("Exception type: ")
|
newMsg.add("Exception type:")
|
||||||
|
|
||||||
# # For debugging purposes
|
# # For debugging purposes
|
||||||
# for entry in getStackTraceEntries(future.error):
|
# for entry in getStackTraceEntries(future.error):
|
||||||
# future.error.msg.add "\n" & $entry
|
# newMsg.add "\n" & $entry
|
||||||
|
future.error.msg = newMsg
|
||||||
|
|
||||||
proc read*[T](future: Future[T] | FutureVar[T]): T =
|
proc read*[T](future: Future[T] | FutureVar[T]): T =
|
||||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,36 @@ discard """
|
||||||
output: '''
|
output: '''
|
||||||
b failure
|
b failure
|
||||||
Async traceback:
|
Async traceback:
|
||||||
tasync_traceback.nim(49) tasync_traceback
|
tasync_traceback.nim(75) tasync_traceback
|
||||||
tasync_traceback.nim(47) a
|
asyncmacro.nim(393) a
|
||||||
tasync_traceback.nim(44) b
|
asyncmacro.nim(43) cb0
|
||||||
|
└─Resumes an async procedure
|
||||||
|
asyncfutures.nim(211) callback=
|
||||||
|
asyncfutures.nim(190) addCallback
|
||||||
|
asyncfutures.nim(53) callSoon
|
||||||
|
asyncmacro.nim(34) cb0
|
||||||
|
└─Resumes an async procedure
|
||||||
|
asyncmacro.nim(0) aIter
|
||||||
|
asyncfutures.nim(355) read
|
||||||
|
tasync_traceback.nim(73) a
|
||||||
|
tasync_traceback.nim(70) b
|
||||||
Exception message: b failure
|
Exception message: b failure
|
||||||
|
Exception type:
|
||||||
|
|
||||||
|
bar failure
|
||||||
|
Async traceback:
|
||||||
|
tasync_traceback.nim(91) tasync_traceback
|
||||||
|
asyncdispatch.nim(1204) waitFor
|
||||||
|
asyncdispatch.nim(1253) poll
|
||||||
|
└─Processes asynchronous completion events
|
||||||
|
asyncdispatch.nim(181) processPendingCallbacks
|
||||||
|
└─Executes pending callbacks
|
||||||
|
asyncmacro.nim(34) cb0
|
||||||
|
└─Resumes an async procedure
|
||||||
|
asyncmacro.nim(0) fooIter
|
||||||
|
asyncfutures.nim(355) read
|
||||||
|
tasync_traceback.nim(86) barIter
|
||||||
|
Exception message: bar failure
|
||||||
Exception type:'''
|
Exception type:'''
|
||||||
"""
|
"""
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
|
|
@ -51,3 +77,18 @@ try:
|
||||||
discard waitFor aFut
|
discard waitFor aFut
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
echo exc.msg
|
echo exc.msg
|
||||||
|
echo()
|
||||||
|
|
||||||
|
# From #6803
|
||||||
|
proc bar(): Future[string] {.async.} =
|
||||||
|
await sleepAsync(100)
|
||||||
|
if true:
|
||||||
|
raise newException(OSError, "bar failure")
|
||||||
|
|
||||||
|
proc foo(): Future[string] {.async.} = return await bar()
|
||||||
|
|
||||||
|
try:
|
||||||
|
echo waitFor(foo())
|
||||||
|
except Exception as exc:
|
||||||
|
echo exc.msg
|
||||||
|
echo()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue