Opened 8 years ago

Last modified 4 months ago

#960 new feature request

Lexical call site string

Reported by: paul@… Owned by:
Priority: normal Milestone:
Component: Compiler Version: 6.6
Keywords: Cc: claus.reinke@…, ndmitchell@…, id@…, pho@…, miffoljud@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty: Unknown
Test Case: N/A Blocked By:
Blocking: Related Tickets:

Description

A function that returns the lexical call site of the current function when an exception occurs.

I'm thinking primarily of "head []", but the same principle would apply to other functions. A dynamic traceback is not necessarily possible in a lazy language, but when "head" gets called on an empty list there must be somewhere in the program that actually said "head", even if it was not evaluated at the time or included in a closure or something. That way when I get a "head: empty list" I don't need to grep my program for "head" calls: I know where head was called from and can start looking there.

Examples:

  • If I just have a function "foo = head" then it will report that the caller was "foo".
  • If I have a function "bar x y = ..." and subsequntly say "baz x = bar (x * 2)" then an exception in "bar" will report the caller as "baz".

I know there is a -x profiler option to get a sort of dynamic traceback, but I find it frequently just tells me the error is in "CAF.List", which isn't very informative.

I'm guessing at a difficulty of 1 week as I don't know the GHC internals. I'm guessing that the call site is going to have to be a hidden parameter passed in to each function call, or alternatively hard-coded when a function is in-lined.

Change History (16)

comment:1 Changed 7 years ago by simonpj

You're asking for "a function"; but what is this function called, and what is its type? In general it's not clear (to me) exactly what you are proposing; or whether it is implementable.

Clarification welcome!

comment:2 Changed 7 years ago by pauljohnson

My first thought was that the head function should include something like

head [] = error $ "head: empty string at " ++ callSite

So then if my file Foo.hs at line 35 says "head []" at run-time I get an error message saying

"head: empty string at Foo.hs line 35"

However this is not referentially transparent: "callSite" is not a constant, and its probably not useful for it to be valid outside exceptions. So instead how about

callSite
Exception -> String

returns the file and line where the function that triggered the exception was called from. Then
the default exception handler can print the exception string followed by the output of callSite.

As Neil Mitchell pointed out in an email, Hat can answer the question of where the erroneous function was called from. But unless you are already using Hat its a lot of trouble to go to just to locate a simple error.

Paul.

comment:3 Changed 7 years ago by simonpj

A neat way to do this might be to extend Template Haskell slightly, to provide

  currentLocation :: Q (Filepath, Int)

giving the filename and location of the current splice. (This is rather similar to the existing Template Haskell function currentModule.)
Then you could define

  callSite :: ExpQ
  callSite = do { (f,n) <- currentLocation
                   ; return (LitE (StringL (f ++ ":" ++ show n))) }

And use it thus:

  error ("woggle at" ++ $callSite)

Since the Template Haskell splice $callSite is a TH splice, it's ok for it to grab contextual information.

comment:4 Changed 7 years ago by pauljohnson

I haven't studied Template Haskell, but won't that just give the location of the "error" call? I want the place the enclosing function was called from. So if "head" calls "error" I want to see what called "head".

comment:5 Changed 7 years ago by simonpj

Indeed ... and you want to see what called whatever called "head", and whatever called that, and so on.

Often asked for, but I don't know how to do that without runtime overhead, passing extra arguments. That's more or less what -xc does.

My suggestion is less ambitious but gets part way.

comment:6 Changed 7 years ago by malcolm.wallace@…

It occurs to me that there may be a relatively simple src->src transformation that could annotate every failing expression with its lexical callsite. Much in the way that hpc uses a src->src transformation to annotate expressions with information about whether (and how often) they get called. One of the cool things about the hpc transformation (in contrast to the more complex src->src transformation done by Hat) is that it does not change the types, so you can freely mix annotated code with plain unannotated code (e.g. from libraries).

I wonder whether you would need to identify "possibly failing" expressions - with something like the Catch analysis tool? Or would it be sufficient as a first cut to just assume that all expressions can fail?

comment:7 Changed 7 years ago by Neil Mitchell

  • Cc ndmitchell@… added

I think the transformation would be simpler than Hat, but would require extra state to be passed round (the stack), so won't be as simple as HPC. I think you could probably write this as a Yhc.Core transformation in a few hours. I may give this a go sometime this week. It should also be pretty easy to add into the Yhc runtime. (I suspect things will be harder in GHC due to more aggressive optimisation, and more complex runtime)

I don't think something like Catch [1] would help narrow down the expressions - Catch identifies multiple separate calls to head, some of which might fail and some of which might pass - an implementation would have to pass failure information if any of them might fail. In addition Catch's transformations are quite heavy, so won't map directly onto the original Haskell. Of course Catch might solve the problem by formal verification, so might answer the question without actually running the program (once its finished...)

[1] http://www-users.cs.york.ac.uk/~ndm/projects/catch.php

comment:8 Changed 7 years ago by igloo

  • Milestone set to _|_
  • Test Case set to N/A

comment:9 Changed 7 years ago by claus

  • Cc claus.reinke@… added; ndmitchell@… removed

since hpc and ghci debugger are now live, does that help with the src->src transformations discussed above? see also

http://hackage.haskell.org/trac/ghc/wiki/ExplicitCallStack

btw, i think the call-stack problems are rooted in the haskell report's translation of function definitions into case without regard to exhaustiveness of the patterns: the translation converts partial functions (which should fail at the call site) into pseudo-exhaustive functions (which can now fail in the callee's code, due to the translation of case).

in spite of these more involved considerations, i'd also like to have a simple way to get at the lexical call site, eg when simulating here-docs, i currently use a hack like this to avoid hard-coding the current file name:

thisFile = catchJust assertions (assert False undefined) getName
  where getName msg = let (_:_:x:_) = reverse $ findIndices (==':') msg
                      in return $ take x msg

comment:10 Changed 6 years ago by igloo

See also #1441

comment:11 Changed 6 years ago by Isaac Dupree

  • Cc claus.reinke@… ndmitchell@… id@… added; claus.reinke@… removed

undefined could use a hack like assert currently does, but as soon as we want to start annotating things like fromJust, we'll need somewhat of a general mechanism. Potentially so worth it though.

comment:12 Changed 6 years ago by simonmar

  • Architecture changed from Unknown to Unknown/Multiple

comment:13 Changed 6 years ago by simonmar

  • Operating System changed from Unknown to Unknown/Multiple

comment:14 Changed 4 years ago by PHO

  • Cc pho@… added

comment:15 Changed 3 years ago by batterseapower

  • Type of failure set to None/Unknown

See also #3693

comment:16 Changed 4 months ago by Tarrasch

  • Cc miffoljud@… added

I think this could be superseded by #3693. Only that it would require compilation with something like -g. There's also multiple overhead-full stack trace implementations today.

It might be time to close this whenever #3693 closes.

Note: See TracTickets for help on using tickets.