Rodrigo Mesquita pushed to branch wip/romes/per-thread-step-in at Glasgow Haskell Compiler / GHC
Commits:
-
ac7b34fd
by Rodrigo Mesquita at 2025-05-23T15:54:43+01:00
11 changed files:
- libraries/ghc-heap/GHC/Exts/Heap/Closures.hs
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghc-heap/tests/parse_tso_flags.hs
- + libraries/ghci/GHCi/Debugger.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Interpreter.c
- rts/Interpreter.h
- rts/RtsSymbols.c
- rts/include/rts/Constants.h
Changes:
... | ... | @@ -624,6 +624,7 @@ data TsoFlags |
624 | 624 | | TsoMarked
|
625 | 625 | | TsoSqueezed
|
626 | 626 | | TsoAllocLimit
|
627 | + | TsoStopNextBreakpoint
|
|
627 | 628 | | TsoFlagsUnknownValue Word32 -- ^ Please report this as a bug
|
628 | 629 | deriving (Eq, Show, Generic, Ord)
|
629 | 630 |
... | ... | @@ -87,6 +87,9 @@ parseTsoFlags w | isSet (#const TSO_LOCKED) w = TsoLocked : parseTsoFlags (unset |
87 | 87 | | isSet (#const TSO_MARKED) w = TsoMarked : parseTsoFlags (unset (#const TSO_MARKED) w)
|
88 | 88 | | isSet (#const TSO_SQUEEZED) w = TsoSqueezed : parseTsoFlags (unset (#const TSO_SQUEEZED) w)
|
89 | 89 | | isSet (#const TSO_ALLOC_LIMIT) w = TsoAllocLimit : parseTsoFlags (unset (#const TSO_ALLOC_LIMIT) w)
|
90 | +#if __GLASGOW_HASKELL__ >= 913
|
|
91 | + | isSet (#const TSO_STOP_NEXT_BREAKPOINT) w = TsoStopNextBreakpoint : parseTsoFlags (unset (#const TSO_STOP_NEXT_BREAKPOINT) w)
|
|
92 | +#endif
|
|
90 | 93 | parseTsoFlags 0 = []
|
91 | 94 | parseTsoFlags w = [TsoFlagsUnknownValue w]
|
92 | 95 |
... | ... | @@ -87,6 +87,9 @@ parseTsoFlags w | isSet (#const TSO_LOCKED) w = TsoLocked : parseTsoFlags (unset |
87 | 87 | | isSet (#const TSO_MARKED) w = TsoMarked : parseTsoFlags (unset (#const TSO_MARKED) w)
|
88 | 88 | | isSet (#const TSO_SQUEEZED) w = TsoSqueezed : parseTsoFlags (unset (#const TSO_SQUEEZED) w)
|
89 | 89 | | isSet (#const TSO_ALLOC_LIMIT) w = TsoAllocLimit : parseTsoFlags (unset (#const TSO_ALLOC_LIMIT) w)
|
90 | +#if __GLASGOW_HASKELL__ >= 913
|
|
91 | + | isSet (#const TSO_STOP_NEXT_BREAKPOINT) w = TsoStopNextBreakpoint : parseTsoFlags (unset (#const TSO_STOP_NEXT_BREAKPOINT) w)
|
|
92 | +#endif
|
|
90 | 93 | parseTsoFlags 0 = []
|
91 | 94 | parseTsoFlags w = [TsoFlagsUnknownValue w]
|
92 | 95 |
... | ... | @@ -13,5 +13,6 @@ main = do |
13 | 13 | assertEqual (parseTsoFlags 64) [TsoMarked]
|
14 | 14 | assertEqual (parseTsoFlags 128) [TsoSqueezed]
|
15 | 15 | assertEqual (parseTsoFlags 256) [TsoAllocLimit]
|
16 | + assertEqual (parseTsoFlags 512) [TsoStopNextBreakpoint]
|
|
16 | 17 | |
17 | 18 | assertEqual (parseTsoFlags 6) [TsoLocked, TsoBlockx] |
1 | +{-# LANGUAGE MagicHash #-}
|
|
2 | +{-# LANGUAGE UnliftedFFITypes #-}
|
|
3 | +module GHCi.Debugger
|
|
4 | + (
|
|
5 | + -- * Single step mode
|
|
6 | + rts_enableStopNextBreakpoint
|
|
7 | + , rts_enableStopNextBreakpointAll
|
|
8 | + , rts_disableStopNextBreakpoint
|
|
9 | + , rts_disableStopNextBreakpointAll
|
|
10 | + |
|
11 | + -- * Stop on exception
|
|
12 | + , exceptionFlag
|
|
13 | + |
|
14 | + -- * Breakpoint Callback
|
|
15 | + , BreakpointCallback
|
|
16 | + , breakPointIOAction
|
|
17 | + ) where
|
|
18 | + |
|
19 | +import Prelude -- See note [Why do we import Prelude here?]
|
|
20 | + |
|
21 | +import GHC.Base (ThreadId#, Addr#, Int#)
|
|
22 | +import Foreign.C (CInt)
|
|
23 | +import Foreign (StablePtr, Ptr)
|
|
24 | +import GHCi.RemoteTypes (HValue)
|
|
25 | + |
|
26 | +--------------------------------------------------------------------------------
|
|
27 | +-- Single step mode
|
|
28 | + |
|
29 | +-- | Enables the single step mode for a specific thread, thus stopping only on
|
|
30 | +-- breakpoints in that thread.
|
|
31 | +foreign import ccall unsafe "rts_enableStopNextBreakpoint"
|
|
32 | + rts_enableStopNextBreakpoint :: ThreadId# -> IO ()
|
|
33 | + |
|
34 | +-- | Disables per-thread single-step mode. Note: if global single-step is
|
|
35 | +-- enabled we stop at all breakpoints regardless of the per-thread flag.
|
|
36 | +foreign import ccall unsafe "rts_disableStopNextBreakpoint"
|
|
37 | + rts_disableStopNextBreakpoint :: ThreadId# -> IO ()
|
|
38 | + |
|
39 | +-- | Enables the single step mode for all threads, thus stopping at any
|
|
40 | +-- existing breakpoint.
|
|
41 | +foreign import ccall unsafe "rts_enableStopNextBreakpointAll"
|
|
42 | + rts_enableStopNextBreakpointAll :: IO ()
|
|
43 | + |
|
44 | +-- | Disables the single step mode for all threads
|
|
45 | +foreign import ccall unsafe "rts_disableStopNextBreakpointAll"
|
|
46 | + rts_disableStopNextBreakpointAll :: IO ()
|
|
47 | + |
|
48 | +--------------------------------------------------------------------------------
|
|
49 | + |
|
50 | +foreign import ccall "&rts_stop_on_exception" exceptionFlag :: Ptr CInt
|
|
51 | + |
|
52 | +--------------------------------------------------------------------------------
|
|
53 | + |
|
54 | +type BreakpointCallback
|
|
55 | + = Addr# -- pointer to the breakpoint tick module name
|
|
56 | + -> Addr# -- pointer to the breakpoint tick module unit id
|
|
57 | + -> Int# -- breakpoint tick index
|
|
58 | + -> Addr# -- pointer to the breakpoint info module name
|
|
59 | + -> Addr# -- pointer to the breakpoint info module unit id
|
|
60 | + -> Int# -- breakpoint info index
|
|
61 | + -> Bool -- exception?
|
|
62 | + -> HValue -- the AP_STACK, or exception
|
|
63 | + -> IO ()
|
|
64 | + |
|
65 | +foreign import ccall "&rts_breakpoint_io_action"
|
|
66 | + breakPointIOAction :: Ptr (StablePtr BreakpointCallback)
|
|
67 | + |
1 | 1 | {-# LANGUAGE GADTs, RecordWildCards, MagicHash, ScopedTypeVariables, CPP,
|
2 | - UnboxedTuples, LambdaCase #-}
|
|
2 | + UnboxedTuples, LambdaCase, UnliftedFFITypes #-}
|
|
3 | 3 | {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
4 | 4 | |
5 | 5 | -- |
|
... | ... | @@ -20,6 +20,7 @@ import GHCi.InfoTable |
20 | 20 | #endif
|
21 | 21 | |
22 | 22 | import qualified GHC.InfoProv as InfoProv
|
23 | +import GHCi.Debugger
|
|
23 | 24 | import GHCi.FFI
|
24 | 25 | import GHCi.Message
|
25 | 26 | import GHCi.ObjLink
|
... | ... | @@ -332,7 +333,7 @@ withBreakAction opts breakMVar statusMVar act |
332 | 333 | stablePtr <- newStablePtr onBreak
|
333 | 334 | poke breakPointIOAction stablePtr
|
334 | 335 | when (breakOnException opts) $ poke exceptionFlag 1
|
335 | - when (singleStep opts) $ setStepFlag
|
|
336 | + when (singleStep opts) rts_enableStopNextBreakpointAll
|
|
336 | 337 | return stablePtr
|
337 | 338 | -- Breaking on exceptions is not enabled by default, since it
|
338 | 339 | -- might be a bit surprising. The exception flag is turned off
|
... | ... | @@ -363,7 +364,7 @@ withBreakAction opts breakMVar statusMVar act |
363 | 364 | resetBreakAction stablePtr = do
|
364 | 365 | poke breakPointIOAction noBreakStablePtr
|
365 | 366 | poke exceptionFlag 0
|
366 | - resetStepFlag
|
|
367 | + rts_disableStopNextBreakpointAll
|
|
367 | 368 | freeStablePtr stablePtr
|
368 | 369 | |
369 | 370 | resumeStmt
|
... | ... | @@ -396,28 +397,6 @@ abandonStmt hvref = do |
396 | 397 | _ <- takeMVar resumeStatusMVar
|
397 | 398 | return ()
|
398 | 399 | |
399 | -foreign import ccall "&rts_stop_next_breakpoint" stepFlag :: Ptr CInt
|
|
400 | -foreign import ccall "&rts_stop_on_exception" exceptionFlag :: Ptr CInt
|
|
401 | - |
|
402 | -setStepFlag :: IO ()
|
|
403 | -setStepFlag = poke stepFlag 1
|
|
404 | -resetStepFlag :: IO ()
|
|
405 | -resetStepFlag = poke stepFlag 0
|
|
406 | - |
|
407 | -type BreakpointCallback
|
|
408 | - = Addr# -- pointer to the breakpoint tick module name
|
|
409 | - -> Addr# -- pointer to the breakpoint tick module unit id
|
|
410 | - -> Int# -- breakpoint tick index
|
|
411 | - -> Addr# -- pointer to the breakpoint info module name
|
|
412 | - -> Addr# -- pointer to the breakpoint info module unit id
|
|
413 | - -> Int# -- breakpoint info index
|
|
414 | - -> Bool -- exception?
|
|
415 | - -> HValue -- the AP_STACK, or exception
|
|
416 | - -> IO ()
|
|
417 | - |
|
418 | -foreign import ccall "&rts_breakpoint_io_action"
|
|
419 | - breakPointIOAction :: Ptr (StablePtr BreakpointCallback)
|
|
420 | - |
|
421 | 400 | noBreakStablePtr :: StablePtr BreakpointCallback
|
422 | 401 | noBreakStablePtr = unsafePerformIO $ newStablePtr noBreakAction
|
423 | 402 |
... | ... | @@ -60,6 +60,7 @@ library |
60 | 60 | CPP-Options: -DHAVE_INTERNAL_INTERPRETER
|
61 | 61 | exposed-modules:
|
62 | 62 | GHCi.Run
|
63 | + GHCi.Debugger
|
|
63 | 64 | GHCi.CreateBCO
|
64 | 65 | GHCi.ObjLink
|
65 | 66 | GHCi.Signals
|
... | ... | @@ -243,9 +243,44 @@ allocate_NONUPD (Capability *cap, int n_words) |
243 | 243 | return allocate(cap, stg_max(sizeofW(StgHeader)+MIN_PAYLOAD_SIZE, n_words));
|
244 | 244 | }
|
245 | 245 | |
246 | -int rts_stop_next_breakpoint = 0;
|
|
247 | 246 | int rts_stop_on_exception = 0;
|
248 | 247 | |
248 | +/* ---------------------------------------------------------------------------
|
|
249 | + * Enabling and disabling global single step mode
|
|
250 | + * ------------------------------------------------------------------------ */
|
|
251 | + |
|
252 | +/* A global toggle for single-step mode.
|
|
253 | + * Unlike `TSO_STOP_NEXT_BREAKPOINT`, which sets single-step mode per-thread,
|
|
254 | + * `rts_stop_next_breakpoint` globally enables single-step mode. If enabled, we
|
|
255 | + * will stop at the immediate next breakpoint regardless of what thread it is in. */
|
|
256 | +int rts_stop_next_breakpoint = 0;
|
|
257 | + |
|
258 | +void rts_enableStopNextBreakpointAll(void)
|
|
259 | +{
|
|
260 | + rts_stop_next_breakpoint = 1;
|
|
261 | +}
|
|
262 | + |
|
263 | +void rts_disableStopNextBreakpointAll(void)
|
|
264 | +{
|
|
265 | + rts_stop_next_breakpoint = 0;
|
|
266 | +}
|
|
267 | + |
|
268 | +/* ---------------------------------------------------------------------------
|
|
269 | + * Enabling and disabling per-thread single step mode
|
|
270 | + * ------------------------------------------------------------------------ */
|
|
271 | + |
|
272 | +void rts_enableStopNextBreakpoint(StgPtr tso)
|
|
273 | +{
|
|
274 | + ((StgTSO *)tso)->flags |= TSO_STOP_NEXT_BREAKPOINT;
|
|
275 | +}
|
|
276 | + |
|
277 | +void rts_disableStopNextBreakpoint(StgPtr tso)
|
|
278 | +{
|
|
279 | + ((StgTSO *)tso)->flags &= ~TSO_STOP_NEXT_BREAKPOINT;
|
|
280 | +}
|
|
281 | + |
|
282 | +/* -------------------------------------------------------------------------- */
|
|
283 | + |
|
249 | 284 | #if defined(INTERP_STATS)
|
250 | 285 | |
251 | 286 | #define N_CODES 128
|
... | ... | @@ -1250,7 +1285,7 @@ run_BCO: |
1250 | 1285 | int arg8_cc;
|
1251 | 1286 | #endif
|
1252 | 1287 | StgArrBytes *breakPoints;
|
1253 | - int returning_from_break;
|
|
1288 | + int returning_from_break, stop_next_breakpoint;
|
|
1254 | 1289 | |
1255 | 1290 | // the io action to run at a breakpoint
|
1256 | 1291 | StgClosure *ioAction;
|
... | ... | @@ -1280,6 +1315,13 @@ run_BCO: |
1280 | 1315 | returning_from_break =
|
1281 | 1316 | cap->r.rCurrentTSO->flags & TSO_STOPPED_ON_BREAKPOINT;
|
1282 | 1317 | |
1318 | + // check whether this thread is set to stop at the immediate next
|
|
1319 | + // breakpoint -- either by the global `rts_stop_next_breakpoint`
|
|
1320 | + // flag, or by the local `TSO_STOP_NEXT_BREAKPOINT`
|
|
1321 | + stop_next_breakpoint =
|
|
1322 | + rts_stop_next_breakpoint ||
|
|
1323 | + cap->r.rCurrentTSO->flags & TSO_STOP_NEXT_BREAKPOINT;
|
|
1324 | + |
|
1283 | 1325 | #if defined(PROFILING)
|
1284 | 1326 | cap->r.rCCCS = pushCostCentre(cap->r.rCCCS,
|
1285 | 1327 | (CostCentre*)BCO_LIT(arg8_cc));
|
... | ... | @@ -1291,20 +1333,20 @@ run_BCO: |
1291 | 1333 | {
|
1292 | 1334 | breakPoints = (StgArrBytes *) BCO_PTR(arg1_brk_array);
|
1293 | 1335 | |
1294 | - // stop the current thread if either the
|
|
1295 | - // "rts_stop_next_breakpoint" flag is true OR if the
|
|
1296 | - // ignore count for this particular breakpoint is zero
|
|
1336 | + // stop the current thread if either `stop_next_breakpoint` is
|
|
1337 | + // true OR if the ignore count for this particular breakpoint is zero
|
|
1297 | 1338 | StgInt ignore_count = ((StgInt*)breakPoints->payload)[arg6_tick_index];
|
1298 | - if (rts_stop_next_breakpoint == false && ignore_count > 0)
|
|
1339 | + if (stop_next_breakpoint == false && ignore_count > 0)
|
|
1299 | 1340 | {
|
1300 | 1341 | // decrement and write back ignore count
|
1301 | 1342 | ((StgInt*)breakPoints->payload)[arg6_tick_index] = --ignore_count;
|
1302 | 1343 | }
|
1303 | - else if (rts_stop_next_breakpoint == true || ignore_count == 0)
|
|
1344 | + else if (stop_next_breakpoint == true || ignore_count == 0)
|
|
1304 | 1345 | {
|
1305 | 1346 | // make sure we don't automatically stop at the
|
1306 | 1347 | // next breakpoint
|
1307 | - rts_stop_next_breakpoint = false;
|
|
1348 | + rts_stop_next_breakpoint = 0;
|
|
1349 | + cap->r.rCurrentTSO->flags &= ~TSO_STOP_NEXT_BREAKPOINT;
|
|
1308 | 1350 | |
1309 | 1351 | // allocate memory for a new AP_STACK, enough to
|
1310 | 1352 | // store the top stack frame plus an
|
... | ... | @@ -11,3 +11,8 @@ |
11 | 11 | RTS_PRIVATE Capability *interpretBCO (Capability* cap);
|
12 | 12 | void interp_startup ( void );
|
13 | 13 | void interp_shutdown ( void );
|
14 | + |
|
15 | +void rts_enableStopNextBreakpointAll ( void );
|
|
16 | +void rts_disableStopNextBreakpointAll ( void );
|
|
17 | +void rts_enableStopNextBreakpoint ( StgPtr );
|
|
18 | +void rts_disableStopNextBreakpoint ( StgPtr ); |
... | ... | @@ -906,7 +906,10 @@ extern char **environ; |
906 | 906 | SymI_HasProto(revertCAFs) \
|
907 | 907 | SymI_HasProto(RtsFlags) \
|
908 | 908 | SymI_NeedsDataProto(rts_breakpoint_io_action) \
|
909 | - SymI_NeedsDataProto(rts_stop_next_breakpoint) \
|
|
909 | + SymI_NeedsDataProto(rts_enableStopNextBreakpointAll) \
|
|
910 | + SymI_NeedsDataProto(rts_disableStopNextBreakpointAll) \
|
|
911 | + SymI_NeedsDataProto(rts_enableStopNextBreakpoint) \
|
|
912 | + SymI_NeedsDataProto(rts_disableStopNextBreakpoint) \
|
|
910 | 913 | SymI_NeedsDataProto(rts_stop_on_exception) \
|
911 | 914 | SymI_HasProto(stopTimer) \
|
912 | 915 | SymI_HasProto(n_capabilities) \
|
... | ... | @@ -328,6 +328,12 @@ |
328 | 328 | */
|
329 | 329 | #define TSO_ALLOC_LIMIT 256
|
330 | 330 | |
331 | +/*
|
|
332 | + * Enables step-in mode for this thread -- it will stop at the immediate next
|
|
333 | + * breakpoint found in this thread.
|
|
334 | + */
|
|
335 | +#define TSO_STOP_NEXT_BREAKPOINT 512
|
|
336 | + |
|
331 | 337 | /*
|
332 | 338 | * The number of times we spin in a spin lock before yielding (see
|
333 | 339 | * #3758). To tune this value, use the benchmark in #3758: run the
|