Hannes Siebenhandl pushed to branch wip/fendor/ann-frame at Glasgow Haskell Compiler / GHC
Commits:
-
1dd1bff4
by Ben Gamari at 2025-05-14T17:19:40+02:00
25 changed files:
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/StgToCmm/Prim.hs
- libraries/ghc-heap/GHC/Exts/Heap/ClosureTypes.hs
- libraries/ghc-heap/GHC/Exts/Heap/Closures.hs
- libraries/ghc-heap/GHC/Exts/Stack.hs
- libraries/ghc-heap/GHC/Exts/Stack/Constants.hsc
- libraries/ghc-heap/GHC/Exts/Stack/Decode.hs
- libraries/ghc-heap/tests/all.T
- + libraries/ghc-heap/tests/ann_frame.hs
- libraries/ghc-internal/src/GHC/Internal/ClosureTypes.hs
- rts/ClosureFlags.c
- rts/LdvProfile.c
- rts/PrimOps.cmm
- rts/Printer.c
- rts/RetainerProfile.c
- rts/TraverseHeap.c
- rts/include/rts/storage/ClosureTypes.h
- rts/include/rts/storage/Closures.h
- rts/js/profiling.js
- rts/sm/Compact.c
- rts/sm/Evac.c
- rts/sm/NonMovingMark.c
- rts/sm/Sanity.c
- rts/sm/Scav.c
- utils/deriveConstants/Main.hs
Changes:
| ... | ... | @@ -3927,6 +3927,16 @@ primop ClearCCSOp "clearCCS#" GenPrimOp |
| 3927 | 3927 | with
|
| 3928 | 3928 | out_of_line = True
|
| 3929 | 3929 | |
| 3930 | +------------------------------------------------------------------------
|
|
| 3931 | +section "Annotating call stacks"
|
|
| 3932 | +------------------------------------------------------------------------
|
|
| 3933 | + |
|
| 3934 | +primop AnnotateStackOp "annotateStack#" GenPrimOp
|
|
| 3935 | + b -> a_reppoly -> a_reppoly
|
|
| 3936 | + { Pushes an annotation frame to the stack which can be reported by backtraces. }
|
|
| 3937 | + with
|
|
| 3938 | + out_of_line = True
|
|
| 3939 | + |
|
| 3930 | 3940 | ------------------------------------------------------------------------
|
| 3931 | 3941 | section "Info Table Origin"
|
| 3932 | 3942 | ------------------------------------------------------------------------
|
| ... | ... | @@ -1771,6 +1771,7 @@ emitPrimOp cfg primop = |
| 1771 | 1771 | WhereFromOp -> alwaysExternal
|
| 1772 | 1772 | GetApStackValOp -> alwaysExternal
|
| 1773 | 1773 | ClearCCSOp -> alwaysExternal
|
| 1774 | + AnnotateStackOp -> alwaysExternal
|
|
| 1774 | 1775 | TraceEventOp -> alwaysExternal
|
| 1775 | 1776 | TraceEventBinaryOp -> alwaysExternal
|
| 1776 | 1777 | TraceMarkerOp -> alwaysExternal
|
| ... | ... | @@ -84,6 +84,7 @@ data ClosureType |
| 84 | 84 | | SMALL_MUT_ARR_PTRS_FROZEN_CLEAN
|
| 85 | 85 | | COMPACT_NFDATA
|
| 86 | 86 | | CONTINUATION
|
| 87 | + | ANN_FRAME
|
|
| 87 | 88 | | N_CLOSURE_TYPES
|
| 88 | 89 | deriving (Enum, Eq, Ord, Show, Generic)
|
| 89 | 90 | #endif
|
| ... | ... | @@ -574,11 +574,15 @@ data GenStackFrame b = |
| 574 | 574 | , retFunPayload :: ![GenStackField b]
|
| 575 | 575 | }
|
| 576 | 576 | |
| 577 | - | RetBCO
|
|
| 577 | + | RetBCO
|
|
| 578 | 578 | { info_tbl :: !StgInfoTable
|
| 579 | 579 | , bco :: !b -- ^ always a BCOClosure
|
| 580 | 580 | , bcoArgs :: ![GenStackField b]
|
| 581 | 581 | }
|
| 582 | + | AnnFrame
|
|
| 583 | + { info_tbl :: !StgInfoTable
|
|
| 584 | + , annotation :: !b
|
|
| 585 | + }
|
|
| 582 | 586 | deriving (Foldable, Functor, Generic, Show, Traversable)
|
| 583 | 587 | |
| 584 | 588 | data PrimType
|
| 1 | 1 | {-# LANGUAGE CPP #-}
|
| 2 | -#if MIN_TOOL_VERSION_ghc(9,9,0)
|
|
| 2 | +#if MIN_TOOL_VERSION_ghc(9,13,0)
|
|
| 3 | 3 | {-# LANGUAGE RecordWildCards #-}
|
| 4 | 4 | |
| 5 | 5 | module GHC.Exts.Stack
|
| ... | ... | @@ -30,6 +30,7 @@ stackFrameSize (RetFun {..}) = sizeStgRetFunFrame + length retFunPayload |
| 30 | 30 | stackFrameSize (RetBCO {..}) = sizeStgClosure + 1 + length bcoArgs
|
| 31 | 31 | -- The one additional word is a pointer to the next stack chunk
|
| 32 | 32 | stackFrameSize (UnderflowFrame {}) = sizeStgClosure + 1
|
| 33 | +stackFrameSize (AnnFrame {}) = sizeStgAnnFrame
|
|
| 33 | 34 | stackFrameSize _ = error "Unexpected stack frame type"
|
| 34 | 35 | |
| 35 | 36 | #else
|
| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
| 4 | 4 | module GHC.Exts.Stack.Constants where
|
| 5 | 5 | |
| 6 | -#if MIN_TOOL_VERSION_ghc(9,9,0)
|
|
| 6 | +#if MIN_TOOL_VERSION_ghc(9,13,0)
|
|
| 7 | 7 | |
| 8 | 8 | import Prelude
|
| 9 | 9 | |
| ... | ... | @@ -88,6 +88,13 @@ offsetStgRetFunFramePayload = byteOffsetToWordOffset (#const OFFSET_StgRetFun_pa |
| 88 | 88 | sizeStgRetFunFrame :: Int
|
| 89 | 89 | sizeStgRetFunFrame = bytesToWords (#const SIZEOF_StgRetFun)
|
| 90 | 90 | |
| 91 | +sizeStgAnnFrame :: Int
|
|
| 92 | +sizeStgAnnFrame = bytesToWords (#const SIZEOF_StgAnnFrame)
|
|
| 93 | + |
|
| 94 | +offsetStgAnnFrameAnn :: WordOffset
|
|
| 95 | +offsetStgAnnFrameAnn = byteOffsetToWordOffset $
|
|
| 96 | + (#const OFFSET_StgAnnFrame_ann) + (#size StgHeader)
|
|
| 97 | + |
|
| 91 | 98 | offsetStgBCOFrameInstrs :: ByteOffset
|
| 92 | 99 | offsetStgBCOFrameInstrs = (#const OFFSET_StgBCO_instrs) + (#size StgHeader)
|
| 93 | 100 |
| 1 | 1 | {-# LANGUAGE CPP #-}
|
| 2 | -#if MIN_TOOL_VERSION_ghc(9,9,0)
|
|
| 2 | +#if MIN_TOOL_VERSION_ghc(9,13,0)
|
|
| 3 | 3 | {-# LANGUAGE BangPatterns #-}
|
| 4 | 4 | {-# LANGUAGE DuplicateRecordFields #-}
|
| 5 | 5 | {-# LANGUAGE FlexibleInstances #-}
|
| ... | ... | @@ -377,6 +377,14 @@ unpackStackFrame (StackSnapshot stackSnapshot#, index) = do |
| 377 | 377 | catchFrameCode = catchFrameCode',
|
| 378 | 378 | handler = handler'
|
| 379 | 379 | }
|
| 380 | + ANN_FRAME ->
|
|
| 381 | + let annotation = getClosureBox stackSnapshot# (index + offsetStgAnnFrameAnn)
|
|
| 382 | + in
|
|
| 383 | + pure $
|
|
| 384 | + AnnFrame
|
|
| 385 | + { info_tbl = info,
|
|
| 386 | + annotation = annotation
|
|
| 387 | + }
|
|
| 380 | 388 | x -> error $ "Unexpected closure type on stack: " ++ show x
|
| 381 | 389 | |
| 382 | 390 | -- | Unbox 'Int#' from 'Int'
|
| ... | ... | @@ -103,3 +103,5 @@ test('stack_misc_closures', |
| 103 | 103 | ]
|
| 104 | 104 | , '-debug' # Debug RTS to use checkSTACK() (Sanity.c)
|
| 105 | 105 | ])
|
| 106 | + |
|
| 107 | +test('ann_frame', normal, compile_and_run, ['']) |
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +{-# LANGUAGE GADTs #-}
|
|
| 3 | + |
|
| 4 | +import Data.Typeable
|
|
| 5 | +import GHC.Exts
|
|
| 6 | +import GHC.Exts.Heap.Closures as Closures
|
|
| 7 | +import GHC.Exts.Stack.Decode
|
|
| 8 | +import GHC.Stack.CloneStack
|
|
| 9 | +import System.IO.Unsafe
|
|
| 10 | +import Unsafe.Coerce
|
|
| 11 | + |
|
| 12 | +data StackAnnotation where
|
|
| 13 | + StackAnnotation :: forall a. (Typeable a, Show a) => a -> StackAnnotation
|
|
| 14 | + |
|
| 15 | +annotateStack
|
|
| 16 | + :: forall a r b.
|
|
| 17 | + (Typeable a, Show a)
|
|
| 18 | + => a -> b -> b
|
|
| 19 | +annotateStack ann =
|
|
| 20 | + annotateStack# (StackAnnotation ann)
|
|
| 21 | + |
|
| 22 | +hello :: Int -> Int -> Int
|
|
| 23 | +hello x y = annotateStack (x,y) $ unsafePerformIO $ do
|
|
| 24 | + stack <- GHC.Stack.CloneStack.cloneMyStack
|
|
| 25 | + decoded <- GHC.Exts.Stack.Decode.decodeStack stack
|
|
| 26 | + print [ show x
|
|
| 27 | + | Closures.AnnFrame _ (Box ann) <- Closures.ssc_stack decoded
|
|
| 28 | + , StackAnnotation x <- pure $ unsafeCoerce ann
|
|
| 29 | + ]
|
|
| 30 | + return $ x + y + 42
|
|
| 31 | +{-# OPAQUE hello #-}
|
|
| 32 | + |
|
| 33 | +main :: IO ()
|
|
| 34 | +main =
|
|
| 35 | + print $ hello 2 3
|
|
| 36 | + |
| ... | ... | @@ -83,5 +83,6 @@ data ClosureType |
| 83 | 83 | | SMALL_MUT_ARR_PTRS_FROZEN_CLEAN
|
| 84 | 84 | | COMPACT_NFDATA
|
| 85 | 85 | | CONTINUATION
|
| 86 | + | ANN_FRAME
|
|
| 86 | 87 | | N_CLOSURE_TYPES
|
| 87 | 88 | deriving (Enum, Eq, Ord, Show, Generic) |
| ... | ... | @@ -88,8 +88,9 @@ const StgWord16 closure_flags[] = { |
| 88 | 88 | [SMALL_MUT_ARR_PTRS_FROZEN_CLEAN] = (_HNF| _NS| _UPT ),
|
| 89 | 89 | [COMPACT_NFDATA] = (_HNF| _NS ),
|
| 90 | 90 | [CONTINUATION] = (_HNF| _NS| _UPT ),
|
| 91 | + [ANN_FRAME] = ( _BTM| _FRM ),
|
|
| 91 | 92 | };
|
| 92 | 93 | |
| 93 | -#if N_CLOSURE_TYPES != 65
|
|
| 94 | +#if N_CLOSURE_TYPES != 66
|
|
| 94 | 95 | #error Closure types changed: update ClosureFlags.c!
|
| 95 | 96 | #endif |
| ... | ... | @@ -154,6 +154,7 @@ processHeapClosureForDead( const StgClosure *c ) |
| 154 | 154 | case CATCH_STM_FRAME:
|
| 155 | 155 | case CATCH_RETRY_FRAME:
|
| 156 | 156 | case ATOMICALLY_FRAME:
|
| 157 | + case ANN_FRAME:
|
|
| 157 | 158 | // others
|
| 158 | 159 | case INVALID_OBJECT:
|
| 159 | 160 | case COMPACT_NFDATA:
|
| ... | ... | @@ -2800,6 +2800,28 @@ stg_clearCCSzh (P_ arg) |
| 2800 | 2800 | jump stg_ap_v_fast(arg);
|
| 2801 | 2801 | }
|
| 2802 | 2802 | |
| 2803 | +#define ANN_FRAME_FIELDS(w_,p_,info_ptr,p1,p2,ann) \
|
|
| 2804 | + w_ info_ptr, \
|
|
| 2805 | + PROF_HDR_FIELDS(w_,p1,p2) \
|
|
| 2806 | + p_ ann
|
|
| 2807 | + |
|
| 2808 | +INFO_TABLE_RET (stg_ann_frame, ANN_FRAME,
|
|
| 2809 | + ANN_FRAME_FIELDS(W_,P_, info_ptr, p1, p2, ann))
|
|
| 2810 | + /* no args => explicit stack */
|
|
| 2811 | +{
|
|
| 2812 | + unwind Sp = W_[Sp + SIZEOF_StgAnnFrame];
|
|
| 2813 | + Sp = Sp + SIZEOF_StgAnnFrame;
|
|
| 2814 | + jump %ENTRY_CODE(Sp(0)) GP_ARG_REGS; // NB. all GP arg regs live!
|
|
| 2815 | +}
|
|
| 2816 | + |
|
| 2817 | +stg_annotateStackzh (P_ ann, P_ cont)
|
|
| 2818 | +{
|
|
| 2819 | + STK_CHK_GEN();
|
|
| 2820 | + jump stg_ap_0_fast
|
|
| 2821 | + (ANN_FRAME_FIELDS(,,stg_ann_frame_info, CCCS, 0, ann))(cont);
|
|
| 2822 | + |
|
| 2823 | +}
|
|
| 2824 | + |
|
| 2803 | 2825 | stg_numSparkszh ()
|
| 2804 | 2826 | {
|
| 2805 | 2827 | W_ n;
|
| ... | ... | @@ -270,6 +270,17 @@ printClosure( const StgClosure *obj ) |
| 270 | 270 | case RET_FUN:
|
| 271 | 271 | */
|
| 272 | 272 | |
| 273 | + case ANN_FRAME:
|
|
| 274 | + {
|
|
| 275 | + StgAnnFrame* frame = (StgAnnFrame*)obj;
|
|
| 276 | + debugBelch("ANN_FRAME(");
|
|
| 277 | + printPtr((StgPtr)GET_INFO((StgClosure *)frame));
|
|
| 278 | + debugBelch(",");
|
|
| 279 | + printPtr((StgPtr)frame->ann);
|
|
| 280 | + debugBelch(")\n");
|
|
| 281 | + break;
|
|
| 282 | + }
|
|
| 283 | + |
|
| 273 | 284 | case UPDATE_FRAME:
|
| 274 | 285 | {
|
| 275 | 286 | StgUpdateFrame* frame = (StgUpdateFrame*)obj;
|
| ... | ... | @@ -1123,6 +1134,7 @@ const char *closure_type_names[] = { |
| 1123 | 1134 | [RET_FUN] = "RET_FUN",
|
| 1124 | 1135 | [UPDATE_FRAME] = "UPDATE_FRAME",
|
| 1125 | 1136 | [CATCH_FRAME] = "CATCH_FRAME",
|
| 1137 | + [ANN_FRAME] = "ANN_FRAME",
|
|
| 1126 | 1138 | [UNDERFLOW_FRAME] = "UNDERFLOW_FRAME",
|
| 1127 | 1139 | [STOP_FRAME] = "STOP_FRAME",
|
| 1128 | 1140 | [BLOCKING_QUEUE] = "BLOCKING_QUEUE",
|
| ... | ... | @@ -1155,7 +1167,7 @@ const char *closure_type_names[] = { |
| 1155 | 1167 | [CONTINUATION] = "CONTINUATION",
|
| 1156 | 1168 | };
|
| 1157 | 1169 | |
| 1158 | -#if N_CLOSURE_TYPES != 65
|
|
| 1170 | +#if N_CLOSURE_TYPES != 66
|
|
| 1159 | 1171 | #error Closure types changed: update Printer.c!
|
| 1160 | 1172 | #endif
|
| 1161 | 1173 |
| ... | ... | @@ -217,6 +217,7 @@ isRetainer( const StgClosure *c ) |
| 217 | 217 | case RET_SMALL:
|
| 218 | 218 | case RET_BIG:
|
| 219 | 219 | case RET_FUN:
|
| 220 | + case ANN_FRAME:
|
|
| 220 | 221 | // other cases
|
| 221 | 222 | case IND:
|
| 222 | 223 | case INVALID_OBJECT:
|
| ... | ... | @@ -529,6 +529,7 @@ traverseGetChildren(StgClosure *c, StgClosure **first_child, bool *other_childre |
| 529 | 529 | case RET_BCO:
|
| 530 | 530 | case RET_SMALL:
|
| 531 | 531 | case RET_BIG:
|
| 532 | + case ANN_FRAME:
|
|
| 532 | 533 | // invalid objects
|
| 533 | 534 | case IND:
|
| 534 | 535 | case INVALID_OBJECT:
|
| ... | ... | @@ -832,6 +833,7 @@ traversePop(traverseState *ts, StgClosure **c, StgClosure **cp, stackData *data, |
| 832 | 833 | case RET_BCO:
|
| 833 | 834 | case RET_SMALL:
|
| 834 | 835 | case RET_BIG:
|
| 836 | + case ANN_FRAME:
|
|
| 835 | 837 | // invalid objects
|
| 836 | 838 | case IND:
|
| 837 | 839 | case INVALID_OBJECT:
|
| ... | ... | @@ -965,6 +967,7 @@ traversePushStack(traverseState *ts, StgClosure *cp, stackElement *sep, |
| 965 | 967 | case CATCH_RETRY_FRAME:
|
| 966 | 968 | case ATOMICALLY_FRAME:
|
| 967 | 969 | case RET_SMALL:
|
| 970 | + case ANN_FRAME:
|
|
| 968 | 971 | bitmap = BITMAP_BITS(info->i.layout.bitmap);
|
| 969 | 972 | size = BITMAP_SIZE(info->i.layout.bitmap);
|
| 970 | 973 | p++;
|
| ... | ... | @@ -89,4 +89,5 @@ |
| 89 | 89 | #define SMALL_MUT_ARR_PTRS_FROZEN_CLEAN 62
|
| 90 | 90 | #define COMPACT_NFDATA 63
|
| 91 | 91 | #define CONTINUATION 64
|
| 92 | -#define N_CLOSURE_TYPES 65 |
|
| 92 | +#define ANN_FRAME 65
|
|
| 93 | +#define N_CLOSURE_TYPES 66 |
| ... | ... | @@ -312,6 +312,15 @@ typedef struct { |
| 312 | 312 | StgClosure *result;
|
| 313 | 313 | } StgDeadThreadFrame;
|
| 314 | 314 | |
| 315 | +// Stack frame annotating an execution context with a Haskell value
|
|
| 316 | +// for backtrace purposes.
|
|
| 317 | +//
|
|
| 318 | +// Closure types: ANN_FRAME
|
|
| 319 | +typedef struct {
|
|
| 320 | + StgHeader header;
|
|
| 321 | + StgClosure *ann;
|
|
| 322 | +} StgAnnFrame;
|
|
| 323 | + |
|
| 315 | 324 | // A function return stack frame: used when saving the state for a
|
| 316 | 325 | // garbage collection at a function entry point. The function
|
| 317 | 326 | // arguments are on the stack, and we also save the function (its
|
| ... | ... | @@ -333,3 +333,8 @@ function h$buildCCSPtr(o) { |
| 333 | 333 | function h$clearCCS(a) {
|
| 334 | 334 | throw new Error("ClearCCSOp not implemented");
|
| 335 | 335 | }
|
| 336 | + |
|
| 337 | +// we throw away the annotation here.
|
|
| 338 | +function h$annotateStack(o) {
|
|
| 339 | + return o;
|
|
| 340 | +} |
| ... | ... | @@ -351,6 +351,7 @@ thread_stack(P_ p, P_ stack_end) |
| 351 | 351 | case STOP_FRAME:
|
| 352 | 352 | case CATCH_FRAME:
|
| 353 | 353 | case RET_SMALL:
|
| 354 | + case ANN_FRAME:
|
|
| 354 | 355 | {
|
| 355 | 356 | W_ bitmap = BITMAP_BITS(info->i.layout.bitmap);
|
| 356 | 357 | W_ size = BITMAP_SIZE(info->i.layout.bitmap);
|
| ... | ... | @@ -996,6 +996,7 @@ loop: |
| 996 | 996 | case CATCH_STM_FRAME:
|
| 997 | 997 | case CATCH_RETRY_FRAME:
|
| 998 | 998 | case ATOMICALLY_FRAME:
|
| 999 | + case ANN_FRAME:
|
|
| 999 | 1000 | // shouldn't see these
|
| 1000 | 1001 | barf("evacuate: stack frame at %p\n", q);
|
| 1001 | 1002 |
| ... | ... | @@ -1180,6 +1180,7 @@ trace_stack_ (MarkQueue *queue, StgPtr sp, StgPtr spBottom) |
| 1180 | 1180 | case STOP_FRAME:
|
| 1181 | 1181 | case CATCH_FRAME:
|
| 1182 | 1182 | case RET_SMALL:
|
| 1183 | + case ANN_FRAME:
|
|
| 1183 | 1184 | {
|
| 1184 | 1185 | StgWord bitmap = BITMAP_BITS(info->i.layout.bitmap);
|
| 1185 | 1186 | StgWord size = BITMAP_SIZE(info->i.layout.bitmap);
|
| ... | ... | @@ -128,6 +128,7 @@ checkStackFrame( StgPtr c ) |
| 128 | 128 | case UNDERFLOW_FRAME:
|
| 129 | 129 | case STOP_FRAME:
|
| 130 | 130 | case RET_SMALL:
|
| 131 | + case ANN_FRAME:
|
|
| 131 | 132 | size = BITMAP_SIZE(info->i.layout.bitmap);
|
| 132 | 133 | checkSmallBitmap((StgPtr)c + 1,
|
| 133 | 134 | BITMAP_BITS(info->i.layout.bitmap), size);
|
| ... | ... | @@ -1983,6 +1983,7 @@ scavenge_stack(StgPtr p, StgPtr stack_end) |
| 1983 | 1983 | case STOP_FRAME:
|
| 1984 | 1984 | case CATCH_FRAME:
|
| 1985 | 1985 | case RET_SMALL:
|
| 1986 | + case ANN_FRAME:
|
|
| 1986 | 1987 | bitmap = BITMAP_BITS(info->i.layout.bitmap);
|
| 1987 | 1988 | size = BITMAP_SIZE(info->i.layout.bitmap);
|
| 1988 | 1989 | // NOTE: the payload starts immediately after the info-ptr, we
|
| ... | ... | @@ -443,6 +443,8 @@ wanteds os = concat |
| 443 | 443 | ,closureSize C "StgStopFrame"
|
| 444 | 444 | ,closureSize C "StgDeadThreadFrame"
|
| 445 | 445 | ,closureField C "StgDeadThreadFrame" "result"
|
| 446 | + ,structSize C "StgAnnFrame"
|
|
| 447 | + ,closureField C "StgAnnFrame" "ann"
|
|
| 446 | 448 | |
| 447 | 449 | ,closureSize Both "StgMutArrPtrs"
|
| 448 | 450 | ,closureField Both "StgMutArrPtrs" "ptrs"
|