Description
Intro
Hello, this is a follow-up to the accepted (unmerged gitlab MR) proposal: #231.
If you will recall, that issue adds the following exception metadata to the default handler:
Main: thread blocked indefinitely in an MVar operation
HasCallStack backtrace:
CallStack (from HasCallStack):
collectBacktraces, called at libraries/base/GHC/Exception.hs:67:13 in base:GHC.Exception
toExceptionWithBacktrace, called at libraries/base/GHC/IO.hs:278:11 in base:GHC.IO
throwIO, called at Main.hs:15:16 in main:Main
-- the below is new
Package: base
Module: GHC.IO.Exception
Name: BlockedIndefinitelyOnMVar
On the issue tracker, @michaelpj observes that the message could be clearer still, with references to other programming languages. At the time I thought this was a good suggestion but didn't think it important enough to warrant the churn of another CLC-proposal and possible interference with the rest of the exceptions work. Now that the "callstacks are part of displayException" work has been merged, however, I think that has changed. In particular, the output with multiple stacktraces / #231 is complicated enough that I think getting other opinions would be wise.
Status quo
As of now, here is what the exception output currently looks like:
Expand example
- Default (
HasCallStack
only)
λ. ./Main
Main: thread blocked indefinitely in an MVar operation
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
throwIO, called at Main.hs:33:16 in main:Main
- All backtraces off
λ. ./Main
Main: thread blocked indefinitely in an MVar operation
- All backtraces on (except DWARF, since that adds an error message w/o other changes)
λ. ./Main
Main: thread blocked indefinitely in an MVar operation
Cost-centre stack backtrace:
IPE backtrace:
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
throwIO, called at Main.hs:33:16 in main:Main
(the trailing newlines are due to there being a "blank" annotation that nonetheless is rendered with separating newlines.)
Current proposal
With the accepted proposal, this looks like:
Expand example
- Default (
HasCallStack
only)
λ. ./Main
Main: thread blocked indefinitely in an MVar operation
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
throwIO, called at Main.hs:40:16 in main:Main
Package: ghc-internal
Module: GHC.Internal.IO.Exception
Name: BlockedIndefinitelyOnMVar
- All backtraces off
λ. ./Main
Main: thread blocked indefinitely in an MVar operation
Package: ghc-internal
Module: GHC.Internal.IO.Exception
Name: BlockedIndefinitelyOnMVar
- All backtraces on
λ. ./Main
Main: thread blocked indefinitely in an MVar operation
Cost-centre stack backtrace:
IPE backtrace:
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
throwIO, called at Main.hs:40:16 in main:Main
Package: ghc-internal
Module: GHC.Internal.IO.Exception
Name: BlockedIndefinitelyOnMVar
Proposed change
Taking inspiration from @michaelpj's suggestion, this could instead look like:
Expand example
- Default (
HasCallStack
only)
λ. ./Main
Main: Exception:
thread blocked indefinitely in an MVar operation
Package: ghc-internal
Module: GHC.Internal.IO.Exception
Type: BlockedIndefinitelyOnMVar
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
throwIO, called at Main.hs:33:16 in main:Main
- All backtraces off
λ. ./Main
Main: Exception:
thread blocked indefinitely in an MVar operation
Package: ghc-internal
Module: GHC.Internal.IO.Exception
Type: BlockedIndefinitelyOnMVar
- All backtraces on (except DWARF)
λ. ./Main
Main: Exception:
thread blocked indefinitely in an MVar operation
Package: ghc-internal
Module: GHC.Internal.IO.Exception
Type: BlockedIndefinitelyOnMVar
Cost-centre stack backtrace:
IPE backtrace:
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/IO.hs:260:11 in ghc-internal:GHC.Internal.IO
throwIO, called at Main.hs:33:16 in main:Main
Notice that there is a new line Exception:
and the exception type info precedes the possibly backtrace info.
The type info could also be more compact e.g. ghc-internal:GHC.Internal.IO.Exception:BlockedIndefinitelyOnMVar
.
Implementation
Moving the exception type info before the backtrace info requires implementing the former in SomeException
's displayException
, not the default handler, in contrast to the accepted proposal. This seems plausible to me, as I could imagine a user wanting to override the handler (e.g. to log exceptions to a file) without losing the displayed type info.
Thanks!