Skip to content

Commit 490db83

Browse files
committed
Nice tracktraces proof of concept
1 parent 693f741 commit 490db83

File tree

1 file changed

+57
-14
lines changed

1 file changed

+57
-14
lines changed

chronos/asyncfutures2.nim

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ type
3535

3636
FutureBase* = ref object of RootObj ## Untyped future.
3737
location: array[2, ptr SrcLoc]
38+
filename: cstring
39+
lineNo: int
40+
procName: cstring
3841
callbacks: Deque[AsyncCallback]
3942
cancelcb*: CallbackFunc
4043
child*: FutureBase
@@ -69,6 +72,9 @@ type
6972
var currentID* {.threadvar.}: int
7073
currentID = 0
7174

75+
proc callerLocation(): PFrame =
76+
getFrame()
77+
7278
# ZAH: This seems unnecessary. Isn't it easy to introduce a seperate
7379
# module for the dispatcher type, so it can be directly referenced here?
7480
var callSoonHolder {.threadvar.}: CallSoonProc
@@ -85,61 +91,64 @@ proc callSoon*(c: CallbackFunc, u: pointer = nil) =
8591
## Call ``cbproc`` "soon".
8692
callSoonHolder(c, u)
8793

88-
template setupFutureBase(loc: ptr SrcLoc) =
94+
template setupFutureBase(loc: ptr SrcLoc, caller: PFrame) =
8995
new(result)
9096
result.state = FutureState.Pending
9197
result.stackTrace = getStackTrace()
9298
result.id = currentID
9399
result.location[LocCreateIndex] = loc
100+
result.filename = caller.prev.filename
101+
result.lineno = caller.prev.line
102+
result.procName = caller.prev.procname
94103
currentID.inc()
95104

96105
## ZAH: As far as I undestand `fromProc` is just a debugging helper.
97106
## It would be more efficient if it's represented as a simple statically
98107
## known `char *` in the final program (so it needs to be a `cstring` in Nim).
99108
## The public API can be defined as a template expecting a `static[string]`
100109
## and converting this immediately to a `cstring`.
101-
proc newFuture[T](loc: ptr SrcLoc): Future[T] =
102-
setupFutureBase(loc)
110+
proc newFuture[T](loc: ptr SrcLoc, srcLoc: PFrame): Future[T] =
111+
setupFutureBase(loc, srcLoc)
103112

104-
proc newFutureSeq[A, B](loc: ptr SrcLoc): FutureSeq[A, B] =
105-
setupFutureBase(loc)
113+
proc newFutureSeq[A, B](loc: ptr SrcLoc, srcLoc: PFrame): FutureSeq[A, B] =
114+
setupFutureBase(loc, srcLoc)
106115

107-
proc newFutureStr[T](loc: ptr SrcLoc): FutureStr[T] =
108-
setupFutureBase(loc)
116+
proc newFutureStr[T](loc: ptr SrcLoc, srcLoc: PFrame): FutureStr[T] =
117+
setupFutureBase(loc, srcLoc)
109118

110-
proc newFutureVar[T](loc: ptr SrcLoc): FutureVar[T] =
111-
FutureVar[T](newFuture[T](loc))
119+
proc newFutureVar[T](loc: ptr SrcLoc, srcLoc: PFrame): FutureVar[T] =
120+
FutureVar[T](newFuture[T](loc, srcLoc))
112121

113122
template newFuture*[T](fromProc: static[string] = ""): auto =
114123
## Creates a new future.
115124
##
116125
## Specifying ``fromProc``, which is a string specifying the name of the proc
117126
## that this future belongs to, is a good habit as it helps with debugging.
118-
newFuture[T](getSrcLocation(fromProc))
127+
newFuture[T](getSrcLocation(fromProc), callerLocation())
119128

120129
template newFutureSeq*[A, B](fromProc: static[string] = ""): auto =
121130
## Create a new future which can hold/preserve GC sequence until future will
122131
## not be completed.
123132
##
124133
## Specifying ``fromProc``, which is a string specifying the name of the proc
125134
## that this future belongs to, is a good habit as it helps with debugging.
126-
newFutureSeq[A, B](getSrcLocation(fromProc))
135+
newFutureSeq[A, B](getSrcLocation(fromProc), callerLocation())
127136

128137
template newFutureStr*[T](fromProc: static[string] = ""): auto =
129138
## Create a new future which can hold/preserve GC string until future will
130139
## not be completed.
131140
##
132141
## Specifying ``fromProc``, which is a string specifying the name of the proc
133142
## that this future belongs to, is a good habit as it helps with debugging.
134-
newFutureStr[T](getSrcLocation(fromProc))
143+
newFutureStr[T](getSrcLocation(fromProc), callerLocation())
135144

136145
template newFutureVar*[T](fromProc: static[string] = ""): auto =
137146
## Create a new ``FutureVar``. This Future type is ideally suited for
138147
## situations where you want to avoid unnecessary allocations of Futures.
139148
##
140149
## Specifying ``fromProc``, which is a string specifying the name of the proc
141150
## that this future belongs to, is a good habit as it helps with debugging.
142-
newFutureVar[T](getSrcLocation(fromProc))
151+
newFutureVar[T](getSrcLocation(fromProc), callerLocation())
143152

144153
proc clean*[T](future: FutureVar[T]) =
145154
## Resets the ``finished`` status of ``future``.
@@ -395,6 +404,40 @@ proc `$`*(entries: seq[StackTraceEntry]): string =
395404
if hint.len > 0:
396405
result.add(spaces(indent+2) & "## " & hint & "\n")
397406

407+
proc getStackTraceEntries(fut: FutureBase): string =
408+
var f = fut
409+
410+
result = "---- Nice stacktrace ------\n"
411+
412+
var topFut = fut
413+
414+
while not f.isNil:
415+
topFut = f
416+
result &= $f.filename & " (" & $f.lineno & ") " & $f.procName & " ---\n"
417+
f = f.child
418+
419+
var stStart = 0
420+
421+
let s = fut.error.getStackTraceEntries()
422+
var i = 1
423+
for e in s:
424+
if e.line == -10: break
425+
if e.procName == "poll":
426+
stStart = i
427+
elif e.filename == topFut.filename and e.line == topFut.lineno:
428+
stStart = i
429+
break
430+
inc i
431+
432+
for i in stStart ..< s.len:
433+
if s[i].line == -10: break
434+
result &= $s[i].filename & " (" & $s[i].line & ") " & $s[i].procname & "\n"
435+
436+
result &= "---- End of nice stacktrace ------\n"
437+
result &= "---- Raw upper exception stacktrace ------\n"
438+
result &= $fut.error.getStackTraceEntries()
439+
result &= "stStart: " & $stStart & "\n"
440+
398441
proc injectStacktrace(future: FutureBase) =
399442
const header = "\nAsync traceback:\n"
400443

@@ -407,7 +450,7 @@ proc injectStacktrace(future: FutureBase) =
407450

408451
var newMsg = exceptionMsg & header
409452

410-
let entries = getStackTraceEntries(future.error)
453+
let entries = getStackTraceEntries(future)
411454
newMsg.add($entries)
412455

413456
newMsg.add("Exception message: " & exceptionMsg & "\n")

0 commit comments

Comments
 (0)