Zubin pushed to branch wip/annotate-frame at Glasgow Haskell Compiler / GHC
Commits:
-
8229e600
by Zubin Duggal at 2026-08-11T13:39:51+05:30
7 changed files:
- libraries/ghc-internal/src/GHC/Internal/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs-boot
- libraries/ghc-internal/src/GHC/Internal/IO.hs
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/Annotation.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs
Changes:
| ... | ... | @@ -75,7 +75,7 @@ import GHC.Internal.Stack.Types |
| 75 | 75 | import GHC.Internal.Types (IO, RuntimeRep)
|
| 76 | 76 | import GHC.Internal.IO.Unsafe
|
| 77 | 77 | import {-# SOURCE #-} GHC.Internal.Stack (prettyCallStackLines, prettyCallStack, prettySrcLoc, withFrozenCallStack)
|
| 78 | -import {-# SOURCE #-} GHC.Internal.Exception.Backtrace (collectExceptionAnnotation)
|
|
| 78 | +import {-# SOURCE #-} GHC.Internal.Exception.Backtrace (collectExceptionContext)
|
|
| 79 | 79 | import GHC.Internal.Exception.Context (SomeExceptionAnnotation(..))
|
| 80 | 80 | import GHC.Internal.Exception.Type
|
| 81 | 81 | |
| ... | ... | @@ -175,11 +175,13 @@ throw e = |
| 175 | 175 | -- @since base-4.20.0.0
|
| 176 | 176 | toExceptionWithBacktrace :: (HasCallStack, Exception e)
|
| 177 | 177 | => e -> IO SomeException
|
| 178 | -toExceptionWithBacktrace e
|
|
| 179 | - | backtraceDesired e = do
|
|
| 180 | - SomeExceptionAnnotation ea <- collectExceptionAnnotation
|
|
| 181 | - return (addExceptionContext ea (toException e))
|
|
| 182 | - | otherwise = return (toException e)
|
|
| 178 | +toExceptionWithBacktrace e = do
|
|
| 179 | + anns <- collectExceptionContext (backtraceDesired e)
|
|
| 180 | + return (applyAnns anns (toException e))
|
|
| 181 | + where
|
|
| 182 | + applyAnns [] se = se
|
|
| 183 | + applyAnns (SomeExceptionAnnotation a : rest) se =
|
|
| 184 | + applyAnns rest (addExceptionContext a se)
|
|
| 183 | 185 | |
| 184 | 186 | -- | This is thrown when the user calls 'error'. The @String@ is the
|
| 185 | 187 | -- argument given to 'error'.
|
| ... | ... | @@ -16,6 +16,8 @@ import GHC.Internal.Maybe (Maybe(..)) |
| 16 | 16 | import GHC.Internal.Ptr
|
| 17 | 17 | import GHC.Internal.Data.Maybe (fromMaybe, mapMaybe)
|
| 18 | 18 | import GHC.Internal.Stack.Types as GHC.Stack (CallStack, HasCallStack)
|
| 19 | +import GHC.Internal.Stack.Annotation (SomeStackAnnotation(..))
|
|
| 20 | +import GHC.Internal.Data.Typeable (cast)
|
|
| 19 | 21 | import qualified GHC.Internal.Stack as HCS
|
| 20 | 22 | import qualified GHC.Internal.ExecutionStack.Internal as ExecStack
|
| 21 | 23 | import qualified GHC.Internal.Stack.CloneStack as CloneStack
|
| ... | ... | @@ -94,12 +96,12 @@ setBacktraceMechanismState bm enabled = do |
| 94 | 96 | -- | How to collect 'ExceptionAnnotation's on throwing 'Exception's.
|
| 95 | 97 | --
|
| 96 | 98 | data CollectExceptionAnnotationMechanism = CollectExceptionAnnotationMechanism
|
| 97 | - { ceaCollectExceptionAnnotationMechanism :: HasCallStack => IO SomeExceptionAnnotation
|
|
| 99 | + { ceaCollectExceptionAnnotationMechanism :: HasCallStack => CloneStack.StackSnapshot -> IO SomeExceptionAnnotation
|
|
| 98 | 100 | }
|
| 99 | 101 | |
| 100 | 102 | defaultCollectExceptionAnnotationMechanism :: CollectExceptionAnnotationMechanism
|
| 101 | 103 | defaultCollectExceptionAnnotationMechanism = CollectExceptionAnnotationMechanism
|
| 102 | - { ceaCollectExceptionAnnotationMechanism = SomeExceptionAnnotation `fmap` collectBacktraces
|
|
| 104 | + { ceaCollectExceptionAnnotationMechanism = \snapshot -> SomeExceptionAnnotation `fmap` collectBacktracesFrom snapshot
|
|
| 103 | 105 | }
|
| 104 | 106 | |
| 105 | 107 | collectExceptionAnnotationMechanismRef :: IORef CollectExceptionAnnotationMechanism
|
| ... | ... | @@ -117,7 +119,7 @@ getCollectExceptionAnnotationMechanism = readIORef collectExceptionAnnotationMec |
| 117 | 119 | setCollectExceptionAnnotation :: ExceptionAnnotation a => (HasCallStack => IO a) -> IO ()
|
| 118 | 120 | setCollectExceptionAnnotation collector = do
|
| 119 | 121 | let cea = CollectExceptionAnnotationMechanism
|
| 120 | - { ceaCollectExceptionAnnotationMechanism = fmap SomeExceptionAnnotation collector
|
|
| 122 | + { ceaCollectExceptionAnnotationMechanism = \_ -> fmap SomeExceptionAnnotation collector
|
|
| 121 | 123 | }
|
| 122 | 124 | _ <- atomicModifyIORef'_ collectExceptionAnnotationMechanismRef (const cea)
|
| 123 | 125 | return ()
|
| ... | ... | @@ -165,18 +167,42 @@ instance ExceptionAnnotation Backtraces where |
| 165 | 167 | --
|
| 166 | 168 | collectExceptionAnnotation :: HasCallStack => IO SomeExceptionAnnotation
|
| 167 | 169 | collectExceptionAnnotation = HCS.withFrozenCallStack $ do
|
| 170 | + snapshot <- CloneStack.cloneMyStack
|
|
| 168 | 171 | cea <- getCollectExceptionAnnotationMechanism
|
| 169 | - ceaCollectExceptionAnnotationMechanism cea
|
|
| 172 | + ceaCollectExceptionAnnotationMechanism cea snapshot
|
|
| 173 | + |
|
| 174 | +stackAnnotationsFrom :: CloneStack.StackSnapshot -> IO [SomeExceptionAnnotation]
|
|
| 175 | +stackAnnotationsFrom snapshot = do
|
|
| 176 | + anns <- CloneStack.decodeStackAnnotations snapshot
|
|
| 177 | + return (mapMaybe (\(SomeStackAnnotation a) -> cast a) anns)
|
|
| 178 | + |
|
| 179 | +collectExceptionContext :: HasCallStack => Bool -> IO [SomeExceptionAnnotation]
|
|
| 180 | +collectExceptionContext backtrace_desired = HCS.withFrozenCallStack $ do
|
|
| 181 | + snapshot <- CloneStack.cloneMyStack
|
|
| 182 | + bt <- if backtrace_desired
|
|
| 183 | + then do
|
|
| 184 | + cea <- getCollectExceptionAnnotationMechanism
|
|
| 185 | + ann <- ceaCollectExceptionAnnotationMechanism cea snapshot
|
|
| 186 | + return [ann]
|
|
| 187 | + else return []
|
|
| 188 | + anns <- stackAnnotationsFrom snapshot
|
|
| 189 | + return (bt ++ anns)
|
|
| 170 | 190 | |
| 171 | 191 | -- | Collect a set of 'Backtraces'.
|
| 172 | 192 | collectBacktraces :: (?callStack :: CallStack) => IO Backtraces
|
| 173 | -collectBacktraces = HCS.withFrozenCallStack $ do
|
|
| 174 | - getEnabledBacktraceMechanisms >>= collectBacktraces'
|
|
| 193 | +collectBacktraces = HCS.withFrozenCallStack $
|
|
| 194 | + CloneStack.cloneMyStack >>= collectBacktracesFrom
|
|
| 195 | + |
|
| 196 | +collectBacktracesFrom
|
|
| 197 | + :: (?callStack :: CallStack)
|
|
| 198 | + => CloneStack.StackSnapshot -> IO Backtraces
|
|
| 199 | +collectBacktracesFrom snapshot = HCS.withFrozenCallStack $ do
|
|
| 200 | + getEnabledBacktraceMechanisms >>= collectBacktraces' snapshot
|
|
| 175 | 201 | |
| 176 | 202 | collectBacktraces'
|
| 177 | 203 | :: (?callStack :: CallStack)
|
| 178 | - => EnabledBacktraceMechanisms -> IO Backtraces
|
|
| 179 | -collectBacktraces' enabled = HCS.withFrozenCallStack $ do
|
|
| 204 | + => CloneStack.StackSnapshot -> EnabledBacktraceMechanisms -> IO Backtraces
|
|
| 205 | +collectBacktraces' snapshot enabled = HCS.withFrozenCallStack $ do
|
|
| 180 | 206 | let collect :: BacktraceMechanism -> IO (Maybe a) -> IO (Maybe a)
|
| 181 | 207 | collect mech f
|
| 182 | 208 | | backtraceMechanismEnabled mech enabled = f
|
| ... | ... | @@ -189,8 +215,7 @@ collectBacktraces' enabled = HCS.withFrozenCallStack $ do |
| 189 | 215 | ExecStack.collectStackTrace
|
| 190 | 216 | |
| 191 | 217 | ipe <- collect IPEBacktrace $ do
|
| 192 | - stack <- CloneStack.cloneMyStack
|
|
| 193 | - return (Just stack)
|
|
| 218 | + return (Just snapshot)
|
|
| 194 | 219 | |
| 195 | 220 | hcs <- collect HasCallStackBacktrace $ do
|
| 196 | 221 | return (Just ?callStack)
|
| ... | ... | @@ -4,8 +4,8 @@ |
| 4 | 4 | module GHC.Internal.Exception.Backtrace where
|
| 5 | 5 | |
| 6 | 6 | import GHC.Internal.Stack.Types (HasCallStack)
|
| 7 | -import GHC.Internal.Types (IO)
|
|
| 7 | +import GHC.Internal.Types (Bool, IO)
|
|
| 8 | 8 | import GHC.Internal.Exception.Context (SomeExceptionAnnotation)
|
| 9 | 9 | |
| 10 | 10 | -- For GHC.Exception
|
| 11 | -collectExceptionAnnotation :: HasCallStack => IO SomeExceptionAnnotation |
|
| 11 | +collectExceptionContext :: HasCallStack => Bool -> IO [SomeExceptionAnnotation] |
| ... | ... | @@ -54,8 +54,9 @@ import GHC.Internal.Classes ( Eq ) |
| 54 | 54 | import GHC.Internal.Magic ( lazy )
|
| 55 | 55 | import GHC.Internal.Maybe ( Maybe(..) )
|
| 56 | 56 | import GHC.Internal.Prim (
|
| 57 | - RealWorld, State#, catch#, getMaskingState#, maskAsyncExceptions#,
|
|
| 58 | - maskUninterruptible#, raiseIO#, unmaskAsyncExceptions#,
|
|
| 57 | + RealWorld, State#, annotateStack#, catch#, getMaskingState#,
|
|
| 58 | + maskAsyncExceptions#, maskUninterruptible#, raiseIO#,
|
|
| 59 | + unmaskAsyncExceptions#,
|
|
| 59 | 60 | )
|
| 60 | 61 | import GHC.Internal.ST
|
| 61 | 62 | import GHC.Internal.Types ( Char, IO(..) )
|
| ... | ... | @@ -65,7 +66,8 @@ import GHC.Internal.Show |
| 65 | 66 | import GHC.Internal.IO.Unsafe
|
| 66 | 67 | import GHC.Internal.Unsafe.Coerce ( unsafeCoerce )
|
| 67 | 68 | |
| 68 | -import GHC.Internal.Exception.Context ( ExceptionAnnotation )
|
|
| 69 | +import GHC.Internal.Exception.Context ( ExceptionAnnotation, SomeExceptionAnnotation(..) )
|
|
| 70 | +import GHC.Internal.Stack.Annotation ( SomeStackAnnotation(..) )
|
|
| 69 | 71 | import GHC.Internal.Stack.Types ( HasCallStack )
|
| 70 | 72 | import {-# SOURCE #-} GHC.Internal.Stack ( withFrozenCallStack )
|
| 71 | 73 | import {-# SOURCE #-} GHC.Internal.IO.Exception ( userError, IOError )
|
| ... | ... | @@ -245,9 +247,8 @@ catchAny !(IO io) handler = IO $ catch# io handler' |
| 245 | 247 | --
|
| 246 | 248 | -- @since base-4.20.0.0
|
| 247 | 249 | annotateIO :: forall e a. ExceptionAnnotation e => e -> IO a -> IO a
|
| 248 | -annotateIO ann (IO io) = IO (catch# io handler)
|
|
| 249 | - where
|
|
| 250 | - handler se = raiseIO# (addExceptionContext ann se)
|
|
| 250 | +annotateIO ann (IO io) =
|
|
| 251 | + IO (annotateStack# (SomeStackAnnotation (SomeExceptionAnnotation ann)) io)
|
|
| 251 | 252 | |
| 252 | 253 | -- Using catchException here means that if `m` throws an
|
| 253 | 254 | -- 'IOError' /as an imprecise exception/, we will not catch
|
| ... | ... | @@ -29,16 +29,17 @@ import GHC.Internal.Base ( |
| 29 | 29 | Monoid(..), Semigroup(..), ap, liftM2, ($), (.),
|
| 30 | 30 | )
|
| 31 | 31 | import GHC.Internal.Classes (Eq(..))
|
| 32 | -import GHC.Internal.Exception (Exception, toExceptionWithBacktrace, fromException, addExceptionContext)
|
|
| 33 | -import GHC.Internal.Exception.Context (ExceptionAnnotation)
|
|
| 32 | +import GHC.Internal.Exception (Exception, toExceptionWithBacktrace, fromException)
|
|
| 33 | +import GHC.Internal.Exception.Context (ExceptionAnnotation, SomeExceptionAnnotation(..))
|
|
| 34 | 34 | import GHC.Internal.Exception.Type (WhileHandling(..))
|
| 35 | 35 | import GHC.Internal.Maybe (Maybe(..))
|
| 36 | 36 | import GHC.Internal.Prim (
|
| 37 | - RealWorld, State#, TVar#, atomically#, catch#, catchRetry#, catchSTM#,
|
|
| 38 | - newTVar#, raiseIO#, readTVar#, readTVarIO#, retry#, writeTVar#,
|
|
| 37 | + RealWorld, State#, TVar#, annotateStack#, atomically#, catchRetry#,
|
|
| 38 | + catchSTM#, newTVar#, raiseIO#, readTVar#, readTVarIO#, retry#, writeTVar#,
|
|
| 39 | 39 | )
|
| 40 | 40 | import GHC.Internal.Prim.PtrEq (sameTVar#)
|
| 41 | 41 | import GHC.Internal.Stack (HasCallStack, withFrozenCallStack)
|
| 42 | +import GHC.Internal.Stack.Annotation (SomeStackAnnotation(..))
|
|
| 42 | 43 | import GHC.Internal.Types (IO(..), isTrue#)
|
| 43 | 44 | |
| 44 | 45 | -- TVars are shared memory locations which support atomic memory
|
| ... | ... | @@ -217,9 +218,8 @@ catchSTM (STM m) handler = STM $ catchSTM# m handler' |
| 217 | 218 | -- | Execute an 'STM' action, adding the given 'ExceptionContext'
|
| 218 | 219 | -- to any thrown synchronous exceptions.
|
| 219 | 220 | annotateSTM :: forall e a. ExceptionAnnotation e => e -> STM a -> STM a
|
| 220 | -annotateSTM ann (STM io) = STM (catch# io handler)
|
|
| 221 | - where
|
|
| 222 | - handler se = raiseIO# (addExceptionContext ann se)
|
|
| 221 | +annotateSTM ann (STM io) =
|
|
| 222 | + STM (annotateStack# (SomeStackAnnotation (SomeExceptionAnnotation ann)) io)
|
|
| 223 | 223 | |
| 224 | 224 | -- |Shared memory locations that support atomic memory transactions.
|
| 225 | 225 | data TVar a = TVar (TVar# RealWorld a)
|
| ... | ... | @@ -4,8 +4,10 @@ module GHC.Internal.Stack.Annotation where |
| 4 | 4 | |
| 5 | 5 | import GHC.Internal.Base (String, (++))
|
| 6 | 6 | import GHC.Internal.Data.Typeable
|
| 7 | +import GHC.Internal.Exception.Context (SomeExceptionAnnotation(..), ExceptionAnnotation(..))
|
|
| 7 | 8 | import GHC.Internal.Maybe (Maybe(..))
|
| 8 | -import GHC.Internal.Stack (SrcLoc, prettySrcLoc)
|
|
| 9 | +import GHC.Internal.Stack.Types (SrcLoc)
|
|
| 10 | +import {-# SOURCE #-} GHC.Internal.Stack (prettySrcLoc)
|
|
| 9 | 11 | |
| 10 | 12 | -- ----------------------------------------------------------------------------
|
| 11 | 13 | -- StackAnnotation
|
| ... | ... | @@ -68,3 +70,7 @@ instance StackAnnotation SomeStackAnnotation where |
| 68 | 70 | |
| 69 | 71 | displayStackAnnotationShort (SomeStackAnnotation a) =
|
| 70 | 72 | displayStackAnnotationShort a
|
| 73 | + |
|
| 74 | +instance StackAnnotation SomeExceptionAnnotation where
|
|
| 75 | + displayStackAnnotationShort (SomeExceptionAnnotation a) =
|
|
| 76 | + displayExceptionAnnotation a |
| ... | ... | @@ -19,6 +19,7 @@ module GHC.Internal.Stack.Decode ( |
| 19 | 19 | decode,
|
| 20 | 20 | decodeStack,
|
| 21 | 21 | decodeStackWithIpe,
|
| 22 | + decodeStackAnnotations,
|
|
| 22 | 23 | -- * Stack decoder helpers
|
| 23 | 24 | decodeStackWithFrameUnpack,
|
| 24 | 25 | -- * StackEntry
|
| ... | ... | @@ -553,6 +554,26 @@ decodeStackWithIpe :: StackSnapshot -> IO [(StackFrame, Maybe InfoProv)] |
| 553 | 554 | decodeStackWithIpe snapshot =
|
| 554 | 555 | concat . snd <$> decodeStackWithFrameUnpack unpackStackFrameWithIpe snapshot
|
| 555 | 556 | |
| 557 | +stackFrameInfoTable :: StackSnapshot# -> WordOffset -> IO StgInfoTable
|
|
| 558 | +stackFrameInfoTable stackSnapshot# index =
|
|
| 559 | + case getInfoTableAddrs# stackSnapshot# (wordOffsetToWord# index) of
|
|
| 560 | + (# itbl#, _ #) -> peekItbl (Ptr itbl#)
|
|
| 561 | + |
|
| 562 | +decodeStackAnnotations :: StackSnapshot -> IO [SomeStackAnnotation]
|
|
| 563 | +decodeStackAnnotations snapshot =
|
|
| 564 | + concat . snd <$> decodeStackWithFrameUnpack unpackAnnotationFrame snapshot
|
|
| 565 | + |
|
| 566 | +unpackAnnotationFrame :: StackFrameLocation -> IO [SomeStackAnnotation]
|
|
| 567 | +unpackAnnotationFrame (StackSnapshot stackSnapshot#, index) = do
|
|
| 568 | + itbl <- stackFrameInfoTable stackSnapshot# index
|
|
| 569 | + case tipe itbl of
|
|
| 570 | + ANN_FRAME ->
|
|
| 571 | + case getClosureBox stackSnapshot# (index + offsetStgAnnFrameAnn) of
|
|
| 572 | + Box ann -> pure [unsafeCoerce ann]
|
|
| 573 | + UNDERFLOW_FRAME ->
|
|
| 574 | + decodeStackAnnotations (getUnderflowFrameNextChunk stackSnapshot# index)
|
|
| 575 | + _ -> pure []
|
|
| 576 | + |
|
| 556 | 577 | -- ----------------------------------------------------------------------------
|
| 557 | 578 | -- Write your own stack decoder!
|
| 558 | 579 | -- ----------------------------------------------------------------------------
|