If I do putStrLn $ prettyCallStack pretty I get something like
CallStack (from HasCallStack): dtraceIO, called at SourceFileModule.hs:34:5 in my-package:SourceFileModule
So I get the location of caller, but I don't get the name of the caller. If caller has HasCallStack, then I get its name but also where it's called from:
CallStack (from HasCallStack): dtraceIO, called at SourceFileModule.hs:34:5 in my-package:SourceFileModule caller, called at OtherModule.hs:34:5 in my-package:OtherModule
but it's not always desirable to add HasCallStack to caller (e.g. it may be recursive). However, the caller name is static.
Could we have the name of the caller be included in the CallStacks, so we could get
CallStack (from HasCallStack): dtraceIO, called at SourceFileModule.hs:34:5 in my-package:SourceFileModule caller
or as some may suggest that current CallStack is off by one, and instead we should have
CallStack (from HasCallStack at dtraceIO): caller at SourceFileModule.hs:34:5 in my-package:SourceFileModule
Edited
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
...
Show closed items
Linked items
0
Link issues together to show that they're related or that one is blocking others.
Learn more.
So I get the location of caller, but I don't get the name of the caller
But what is the "name of the caller"? For example
f xs = blah blah where g y = (t,t) where t = map (\p -> h (p+1)) xs -- Say this is line 10 of Foo.hsh :: HasCallStack => blah
Currently, if h crashes, we get a call stack saying
h called at Foo.hs:10
Now, what is the "name of the caller"? Is it
The lambda (\p -> ...)
map
t
g
f
It's not at all clear to me.
In contrast the source location precisely specifies the call site, and you can decide what you think of as "the caller". What extra information would really be added by appending a tricky-to-specify caller?
You may be on to something but I don't yet see it.
For what it's worth, I've wanted this also; my debuggable package (there is also a draft blogpost ) goes through a bit of effort to get this information; its answer to Simon's first question "what is the caller" is: whatever comes next element in the callstack.
As for Simon's second question, "What extra information would really be added by appending a tricky-to-specify caller?", my answer at least is primarily readability of long debugging logs; yes, the information is there in the code (line X refers to function F), but if you're reading a long log, it's quite helpful to see "f called g, then g called h" (which is the kind of thing debuggable will tell you).
its answer to Simon's first question "what is the caller" is: whatever comes next element in the callstack
I see. So instead of
CallStack (from HasCallStack): wombat, called at SourceFileModule.hs:34:5 thisfun, called at OtherModule.hs:34:5 thatfun, called at ThatModule:88
you'd like to see
CallStack (from HasCallStack): wombat, called by thisfun at SourceFileModule.hs:34:5 thisfun, called by thatfun at OtherModule.hs:34:5 thatfun, called at ThatModule:88
Is that right? On each line, replicate the name at the start of the next line. (Note that the last line has no such caller because there is no next element.)
Have I correctly understood? I suppose that would be possible. But perhaps others can jump in to say whether they would find such a change welcome? (To me it looks doubtful.)
if you're reading a long log, it's quite helpful to see "f called g, then g called h" (which is the kind of thing debuggable will tell you
Agreed. And isn't that precisely what the current log gives you? In
CallStack (from HasCallStack): wombat, called at SourceFileModule.hs:34:5 thisfun, called at OtherModule.hs:34:5 thatfun, called at ThatModule:88
looking down that first column I see wombat called by thisfun called by thatfun. I feel I must be missing something important.
CallStack (from HasCallStack): wombat, called at SourceFileModule.hs:34:5 thisfun, called at OtherModule.hs:34:5 thatfun, called at ThatModule:88
you'd like to see
CallStack (from HasCallStack): wombat, called by thisfun at SourceFileModule.hs:34:5 thisfun, called by thatfun at OtherModule.hs:34:5 thatfun, called at ThatModule:88
No. I want to see what called thatfun, i.e.:
CallStack (from HasCallStack): wombat, called by thisfun at SourceFileModule.hs:34:5 thisfun, called by thatfun at OtherModule.hs:34:5 thatfun, called at ThatModule:88 otherFunctionInThatModule
Yes, I agree this can all be done user-side (indeed, I have done just that).
I didn't mean to ask "could the user not do this". Rather I'm trying to better understand what "this" is, and why it's an improvement. After all, if there is an easy improvement to make, let's make it!
Specifically
I believe you prefer LogB to LogA (see above). But to me they look very similar. Can you confirm that I have correctly understood what you want? Why does LogB seem better?
You say "it's quite helpful to see "f called g, then g called h"". But that seems just as visible in LogA as in LogB, indeed perhaps even more because there is less clutter. How does LogB tell you that more clearly?
(Of course these questions are subjective, and it's fine for you to say "I just find LogB better".)
I can't answer for the OP. Perhaps one reason is that if either thisfun or thatfun doesn't have a HasCallStack annotation, it's not so obvious that you could get the information you need by adding it? (In debuggable you would see {unknown} -> wombat in this case, and the docs say what to do with such an {unknown} case). For long debugging logs the full callstack is also too much information; you'd want to see "f called g, then later foo called bar, then this called that". All very much depends on the use case of course.
But I'll let OP answer further :) I just thought I'd chime in because I've thought about these issues recently working on debuggable.
Perhaps one reason is that if either thisfun or thatfun doesn't have a HasCallStack annotation, it's not so obvious that you could get the information you need by adding it?
Ahhh... but then you are speaking about the bottom-most element of the call stack, so we can't appeal to your " whatever comes next element in the callstack" specification for the "caller". And that puts us back to the question "what precisely is the 'caller'?" for the bottom-most element.
Anyway, let's see if others have improvements to suggest.
it's not so obvious that you could get the information you need by adding it
As I mention in OP description, sometimes you don't want that.
importGHC.Stackfatal::HasCallStack=>String->IO()fatalerr=fail$"fatal "++err++" "++prettyCallStackcallStack-- | It's not desirable to have HasCallStack on application,-- as it's recursive.---- However, it's the only way to get "application" to be printed in callStackapplication::Int->IO()applicationn|n<0=fatal"negative"|otherwise=application(n-1)
If I run application 10 I get,
*Main> application 10*** Exception: user error (fatal negative CallStack (from HasCallStack): fatal, called at ex.hs:12:15 in main:Main)
No mention of application.
If I add HasCallStack, i.e. application :: HasCallStack => Int -> IO (), I get
*Main> application 10*** Exception: user error (fatal negative CallStack (from HasCallStack): fatal, called at ex.hs:12:15 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at ex.hs:13:19 in main:Main application, called at <interactive>:2:1 in interactive:Ghci1)
Which is too much. I Only wanted to know that fatal, called at ex.hs:12:15 in main:Main.application. (Note the Main.application).
In particular, at this point I don't care strongly how the prettyCallStack formats the call stack. The last element is missing, and that's the main issue.
I see the callstack as "node-edge" structure, with nodes being binding names and edges the locations at which call occurs. The last node is missing
foo --(calls at some location)--> bar --(calls at other location)--> quu
It's not [(String, SrcLoc)] but rather ([(String, SrcLoc)], String) or (String, [(SrcLoc, String)]) (depending on how you think about it, but those are isomorphic).
I think the simplest solution to get this forward is to use the top-level names.
For debugging purposes it's enough, and I don't think people pay as much attention
to local names.
If this choice turns out to not be great, it can be changed. Or more control could be given to users, i.e. have flags similar to -fprof-auto, -fprof-auto-top, -fprof-exported and -fprof-all (which all make sense for the HasCallStack bottom element too).
Relatedly, these do affect currentCallStack and maybe someone is actually using these profiling flags to refine their debugging experience. (i.e. they aren't actually profiling, but debugging and using curreentCallStack).
I think the top-level name is good. If doThat is desired, then the easiest solution is simply to then also give it a (manual) HasCallStack constraint, so that we'd see topLevel -> doThat -> ...