Make async stacktraces less verbose (#21091)

* Name iterators something human readable

Remove intermediate async procs from stacktraces

Clean async traceback message from reraises message

* Remove unused import/variable

* Fix failing tests

Don't add {.stackTrace: off.} to anonymous procs (They already don't appear in stacktrace)

* Fix failing tests in pragma category

Now check that the nim is a routine type first so we don't run into any assertion defects

* Hide stack trace pragma in docs and update doc tests

User doesn't need to know if something won't appear so this more becomes verbose noise

If this is a bad idea we can always add a `when defined(nimdoc)` switch so we don't add {.stackTrace: off.} to the Future[T] returning proc for docs
This commit is contained in:
Jake Leahy 2022-12-16 02:05:34 +11:00 • committed by GitHub
commit d88f46df38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 63 deletions

View file

@ -619,7 +619,9 @@ proc isHideable(config: ConfigRef, n: PNode): bool =
# xxx compare `ident` directly with `getIdent(cache, wRaises)`, but # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but
# this requires a `cache`. # this requires a `cache`.
case n.kind case n.kind
of nkExprColonExpr: result = n[0].kind == nkIdent and n[0].ident.s in ["raises", "tags", "extern", "deprecated", "forbids"] of nkExprColonExpr:
result = n[0].kind == nkIdent and
n[0].ident.s.nimIdentNormalize in ["raises", "tags", "extern", "deprecated", "forbids", "stacktrace"]
of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"] of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"]
else: result = false else: result = false

View file

@ -329,29 +329,21 @@ proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
if leftLen > longestLeft: if leftLen > longestLeft:
longestLeft = leftLen longestLeft = leftLen
var indent = 2
# Format the entries. # Format the entries.
for entry in entries: for entry in entries:
let (filename, procname) = getFilenameProcname(entry) let (filename, procname) = getFilenameProcname(entry)
if procname == "": if procname == "" and entry.line == reraisedFromBegin:
if entry.line == reraisedFromBegin: break
result.add(spaces(indent) & "#[\n")
indent.inc(2)
elif entry.line == reraisedFromEnd:
indent.dec(2)
result.add(spaces(indent) & "]#\n")
continue
let left = "$#($#)" % [filename, $entry.line] let left = "$#($#)" % [filename, $entry.line]
result.add((spaces(indent) & "$#$# $#\n") % [ result.add((spaces(2) & "$# $#\n") % [
left, left,
spaces(longestLeft - left.len + 2),
procname procname
]) ])
let hint = getHint(entry) let hint = getHint(entry)
if hint.len > 0: if hint.len > 0:
result.add(spaces(indent+2) & "## " & hint & "\n") result.add(spaces(4) & "## " & hint & "\n")
proc injectStacktrace[T](future: Future[T]) = proc injectStacktrace[T](future: Future[T]) =
when not defined(release): when not defined(release):

View file

@ -20,9 +20,8 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN
template createCb(retFutureSym, iteratorNameSym, template createCb(retFutureSym, iteratorNameSym,
strName, identName, futureVarCompletions: untyped) = strName, identName, futureVarCompletions: untyped) =
bind finished bind finished
var nameIterVar = iteratorNameSym var nameIterVar = iteratorNameSym
proc identName {.closure.} = proc identName {.closure, stackTrace: off.} =
try: try:
if not nameIterVar.finished: if not nameIterVar.finished:
var next = nameIterVar() var next = nameIterVar()
@ -151,6 +150,10 @@ proc asyncSingleProc(prc: NimNode): NimNode =
result[0][0] = quote do: Future[void] result[0][0] = quote do: Future[void]
return result return result
if prc.kind in RoutineNodes and prc.name.kind != nnkEmpty:
# Only non anonymous functions need/can have stack trace disabled
prc.addPragma(nnkExprColonExpr.newTree(ident"stackTrace", ident"off"))
if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
error("Cannot transform this node kind into an async proc." & error("Cannot transform this node kind into an async proc." &
" proc/method definition or lambda node expected.", prc) " proc/method definition or lambda node expected.", prc)
@ -209,7 +212,7 @@ proc asyncSingleProc(prc: NimNode): NimNode =
# -> {.pop.} # -> {.pop.}
# -> <proc_body> # -> <proc_body>
# -> complete(retFuture, result) # -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prcName & "Iter") var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)")
var procBody = prc.body.processBody(retFutureSym, futureVarIdents) var procBody = prc.body.processBody(retFutureSym, futureVarIdents)
# don't do anything with forward bodies (empty) # don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty: if procBody.kind != nnkEmpty:
@ -251,9 +254,10 @@ proc asyncSingleProc(prc: NimNode): NimNode =
# friendlier stack traces: # friendlier stack traces:
var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix) var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix)
var procCb = getAst createCb(retFutureSym, iteratorNameSym, var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prcName), newStrLitNode(prcName),
cbName, cbName,
createFutureVarCompletions(futureVarIdents, nil)) createFutureVarCompletions(futureVarIdents, nil)
)
outerProcBody.add procCb outerProcBody.add procCb
# -> return retFuture # -> return retFuture

View file

@ -503,7 +503,8 @@
</div> </div>
<div id="asyncFun1-procs-all"> <div id="asyncFun1-procs-all">
<div id="asyncFun1"> <div id="asyncFun1">
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span> <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span>
<span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd> <dd>
@ -515,8 +516,8 @@
</div> </div>
<div id="asyncFun2-procs-all"> <div id="asyncFun2-procs-all">
<div id="asyncFun2"> <div id="asyncFun2">
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd> <dd>
@ -527,8 +528,8 @@
</div> </div>
<div id="asyncFun3-procs-all"> <div id="asyncFun3-procs-all">
<div id="asyncFun3"> <div id="asyncFun3">
<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
<span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd> <dd>

View file

@ -1,5 +1,5 @@
discard """ discard """
errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'" errormsg: "'anotherGCSafeAsyncProc (Async)' is not GC-safe as it calls 'asyncGCUnsafeProc'"
cmd: "nim c --threads:on $file" cmd: "nim c --threads:on $file"
file: "asyncmacro.nim" file: "asyncmacro.nim"
""" """

View file

@ -67,51 +67,22 @@ import re
const expected = """ const expected = """
b failure b failure
Async traceback: Async traceback:
tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback tasync_traceback\.nim\(\d+?\) tasync_traceback
asyncmacro\.nim\(\d+?\)\s+?a tasync_traceback\.nim\(\d+?\) a \(Async\)
asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue tasync_traceback\.nim\(\d+?\) b \(Async\)
## Resumes an async procedure
tasync_traceback\.nim\(\d+?\)\s+?aIter
asyncmacro\.nim\(\d+?\)\s+?b
asyncmacro\.nim\(\d+?\)\s+?bNimAsyncContinue
## Resumes an async procedure
tasync_traceback\.nim\(\d+?\)\s+?bIter
#\[
tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
asyncmacro\.nim\(\d+?\)\s+?a
asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue
## Resumes an async procedure
asyncmacro\.nim\(\d+?\)\s+?aIter
asyncfutures\.nim\(\d+?\)\s+?read
\]#
Exception message: b failure Exception message: b failure
bar failure bar failure
Async traceback: Async traceback:
tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback tasync_traceback\.nim\(\d+?\) tasync_traceback
asyncdispatch\.nim\(\d+?\)\s+?waitFor asyncdispatch\.nim\(\d+?\) waitFor
asyncdispatch\.nim\(\d+?\)\s+?poll asyncdispatch\.nim\(\d+?\) poll
## Processes asynchronous completion events ## Processes asynchronous completion events
asyncdispatch\.nim\(\d+?\)\s+?runOnce asyncdispatch\.nim\(\d+?\) runOnce
asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks asyncdispatch\.nim\(\d+?\) processPendingCallbacks
## Executes pending callbacks ## Executes pending callbacks
asyncmacro\.nim\(\d+?\)\s+?barNimAsyncContinue tasync_traceback\.nim\(\d+?\) bar \(Async\)
## Resumes an async procedure
tasync_traceback\.nim\(\d+?\)\s+?barIter
#\[
tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
asyncdispatch\.nim\(\d+?\)\s+?waitFor
asyncdispatch\.nim\(\d+?\)\s+?poll
## Processes asynchronous completion events
asyncdispatch\.nim\(\d+?\)\s+?runOnce
asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks
## Executes pending callbacks
asyncmacro\.nim\(\d+?\)\s+?fooNimAsyncContinue
## Resumes an async procedure
asyncmacro\.nim\(\d+?\)\s+?fooIter
asyncfutures\.nim\(\d+?\)\s+?read
\]#
Exception message: bar failure Exception message: bar failure
""" """