Allow users to customise stack decoders in `Backtraces`
## Motivation
[`Backtraces`](https://hackage.haskell.org/package/ghc-internal-9.1201.0/docs/src/GHC.Internal.Exception.Backtrace.html#Backtraces) are used to print stack traces when exceptions are thrown.
The user can currently not easily change how the backtraces are actually decoded from a given snapshot, which makes it more difficult to experiment with user-defined stack decoders.
For example, it would be nice to use https://hackage.haskell.org/package/ghc-heap-9.12.1/docs/GHC-Exts-Stack.html#v:decodeStack for stack decoding due to #24811, but other stack decoders are also realistic.
## Proposal
It would be nice if users can specify how the backtraces are collected and decoded.
## Summary of changes
* Change `Backtraces` to use more general `StackSnapshot` and `StackTrace` instead of already decoded version
* Introduce global `IORef` for defining which `ExceptionAnnotation`s are collected during exception throwing
* Change `toExceptionWithBacktrace` to collect `ExceptionAnnotation` via the `IORef`
## Implementation
We propose to change the type of `Backtraces` from:
```haskell
data Backtraces =
Backtraces {
btrCostCentre :: Maybe (Ptr CCS.CostCentreStack),
btrHasCallStack :: Maybe HCS.CallStack,
btrExecutionStack :: Maybe [ExecStack.Location],
btrIpe :: Maybe [CloneStack.StackEntry]
}
```
to:
```haskell
data Backtraces =
Backtraces {
btrCostCentre :: Maybe (Ptr CCS.CostCentreStack),
btrHasCallStack :: Maybe HCS.CallStack,
btrExecutionStack :: Maybe ExecStack.StackTrace,
btrIpe :: Maybe CloneStack.StackSnapshot,
}
```
* `btrIpe` and `btrExecutionStack` use the more general `StackSnapshot` and `StackTrace` respectively, to give the user a higher flexibility.
Similarly to [BacktraceMechanism](https://hackage.haskell.org/package/ghc-internal-9.1201.0/docs/GHC-Internal-Exception-Backtrace.html#t:BacktraceMechanism), we introduce
```haskell
data CollectExceptionAnnotationMechanism = CollectExceptionAnnotationMechanism
{ ceaCollectExceptionAnnotationMechanism :: HasCallStack => IO SomeExceptionAnnotation
}
```
which is stored in a global `IORef` to allow the user to overwrite the default stack decoding logic.
This type is paired with appropriate setter and getter functions.
The function provided in `ceaCollectExceptionAnnotationMechanism` is then used in
```haskell
collectExceptionAnnotation :: HasCallStack => IO SomeExceptionAnnotation
collectExceptionAnnotation = HCS.withFrozenCallStack $ do
cea <- getCollectExceptionAnnotationMechanism
ceaCollectExceptionAnnotationMechanism cea
```
which replaces the less general `collectBacktraces`. We use `collectExceptionAnnotation` instead of `collectBacktraces` in
```haskell
toExceptionWithBacktrace :: (HasCallStack, Exception e)
=> e -> IO SomeException
toExceptionWithBacktrace e
| backtraceDesired e = do
ea <- collectExceptionAnnotation
return (addExceptionContext ea (toException e))
| otherwise = return (toException e)
```
issue