mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 17:34:43 +00:00
Implement some simple pattern-based transformation for async tracebacks.
This commit is contained in:
committed by
Andreas Rumpf
parent
f3a895f043
commit
f73015ad9e
@@ -217,17 +217,76 @@ proc `callback=`*[T](future: Future[T],
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
future.callback = proc () = cb(future)
|
||||
|
||||
proc injectStacktrace[T](future: Future[T]) =
|
||||
# TODO: Come up with something better.
|
||||
when not defined(release):
|
||||
var msg = ""
|
||||
msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:")
|
||||
|
||||
if not future.errorStackTrace.isNil and future.errorStackTrace != "":
|
||||
msg.add("\n" & indent(future.errorStackTrace.strip(), 4))
|
||||
proc processEntries(entries: seq[StackTraceEntry]): seq[StackTraceEntry] =
|
||||
proc get(entries: seq[StackTraceEntry], i: int): StackTraceEntry =
|
||||
if i >= entries.len:
|
||||
return StackTraceEntry(procName: "", line: 0, filename: "")
|
||||
else:
|
||||
msg.add("\n Empty or nil stack trace.")
|
||||
future.error.msg.add(msg)
|
||||
return entries[i]
|
||||
|
||||
result = @[]
|
||||
var i = 0
|
||||
while i < entries.len:
|
||||
var entry = entries[i]
|
||||
|
||||
if entry.procName.isNil:
|
||||
# Start of a re-raise traceback which we do not care for.
|
||||
break
|
||||
|
||||
# Detect this pattern:
|
||||
# (procname: a, line: 393, filename: asyncmacro.nim)
|
||||
# (procname: cb0, line: 34, filename: asyncmacro.nim)
|
||||
# (procname: aIter, line: 40, filename: tasync_traceback.nim)
|
||||
let second = get(entries, i+1)
|
||||
let third = get(entries, i+2)
|
||||
let fitsPattern =
|
||||
cmpIgnoreStyle($entry.filename, "asyncmacro.nim") == 0 and
|
||||
cmpIgnoreStyle($second.filename, "asyncmacro.nim") == 0 and
|
||||
cmpIgnoreStyle($second.procName, "cb0") == 0 and
|
||||
cmpIgnoreStyle($third.procName, $entry.procName & "iter") == 0
|
||||
|
||||
if fitsPattern:
|
||||
entry = StackTraceEntry(
|
||||
procName: entry.procName,
|
||||
line: third.line,
|
||||
filename: third.filename
|
||||
)
|
||||
i.inc(2)
|
||||
|
||||
result.add(entry)
|
||||
i.inc
|
||||
|
||||
proc injectStacktrace[T](future: Future[T]) =
|
||||
when not defined(release):
|
||||
const header = "Async traceback\n---------------\n"
|
||||
|
||||
let originalMsg = future.error.msg
|
||||
if header in originalMsg:
|
||||
return
|
||||
|
||||
let entries = getStackTraceEntries(future.error).processEntries()
|
||||
future.error.msg = "\n" & header
|
||||
|
||||
# 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
|
||||
|
||||
# Format the entries.
|
||||
for entry in entries:
|
||||
let left = "$#($#)" % [$entry.filename, $entry.line]
|
||||
future.error.msg.add("$1$2 $3\n" % [
|
||||
left,
|
||||
spaces(longestLeft - left.len + 2), $entry.procName])
|
||||
|
||||
future.error.msg.add("Exception message: " & originalMsg & "\n")
|
||||
future.error.msg.add("Exception type: ")
|
||||
|
||||
# For debugging purposes TODO...
|
||||
for entry in getStackTraceEntries(future.error):
|
||||
future.error.msg.add "\n" & $entry
|
||||
|
||||
proc read*[T](future: Future[T] | FutureVar[T]): T =
|
||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||
|
||||
46
tests/async/tasync_traceback.nim
Normal file
46
tests/async/tasync_traceback.nim
Normal file
@@ -0,0 +1,46 @@
|
||||
discard """
|
||||
exitcode: 0
|
||||
output: ""
|
||||
"""
|
||||
import asyncdispatch
|
||||
|
||||
# Tests to ensure our exception trace backs are friendly.
|
||||
|
||||
# --- Simple test. ---
|
||||
#
|
||||
# What does this look like when it's synchronous?
|
||||
#
|
||||
# tasync_traceback.nim(23) tasync_traceback
|
||||
# tasync_traceback.nim(21) a
|
||||
# tasync_traceback.nim(18) b
|
||||
# Error: unhandled exception: b failure [OSError]
|
||||
#
|
||||
# Good (not quite ideal, but gotta work within constraints) traceback,
|
||||
# when exception is unhandled:
|
||||
#
|
||||
# <traceback for the unhandled exception>
|
||||
# <very much a bunch of noise>
|
||||
# <would be ideal to customise this>
|
||||
# <(the code responsible is in excpt:raiseExceptionAux)>
|
||||
# Error: unhandled exception: b failure
|
||||
# ===============
|
||||
# Async traceback
|
||||
# ===============
|
||||
#
|
||||
# tasync_traceback.nim(23) tasync_traceback
|
||||
#
|
||||
# tasync_traceback.nim(21) a
|
||||
# tasync_traceback.nim(18) b
|
||||
|
||||
proc b(): Future[int] {.async.} =
|
||||
if true:
|
||||
raise newException(OSError, "b failure")
|
||||
|
||||
proc a(): Future[int] {.async.} =
|
||||
return await b()
|
||||
|
||||
let aFut = a()
|
||||
# try:
|
||||
discard waitFor aFut
|
||||
# except Exception as exc:
|
||||
# echo exc.msg
|
||||
Reference in New Issue
Block a user