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
6972var currentID* {.threadvar .}: int
7073currentID = 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?
7480var 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
113122template 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
120129template 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
128137template 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
136145template 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
144153proc 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+
398441proc injectStacktrace (future: FutureBase ) =
399442 const header = " \n Async 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