Rodrigo Mesquita pushed to branch wip/romes/per-thread-step-in at Glasgow Haskell Compiler / GHC
Commits:
-
695b5398
by Rodrigo Mesquita at 2025-05-19T13:59:05+01:00
6 changed files:
- libraries/ghc-heap/GHC/Exts/Heap/Closures.hs
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghci/GHCi/Run.hs
- rts/Interpreter.c
- 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,7 @@ 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 | + | isSet (#const TSO_STOP_NEXT_BREAKPOINT) w = TsoStopNextBreakpoint : parseTsoFlags (unset (#const TSO_STOP_NEXT_BREAKPOINT) w)
|
|
90 | 91 | parseTsoFlags 0 = []
|
91 | 92 | parseTsoFlags w = [TsoFlagsUnknownValue w]
|
92 | 93 |
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 | -- |
|
... | ... | @@ -396,13 +396,21 @@ abandonStmt hvref = do |
396 | 396 | _ <- takeMVar resumeStatusMVar
|
397 | 397 | return ()
|
398 | 398 | |
399 | -foreign import ccall "&rts_stop_next_breakpoint" stepFlag :: Ptr CInt
|
|
399 | +foreign import ccall unsafe "rts_enableStopNextBreakpointAll"
|
|
400 | + rts_enableStopNextBreakpointAll :: IO ()
|
|
401 | + |
|
402 | +foreign import ccall unsafe "rts_disableStopNextBreakpointAll"
|
|
403 | + rts_disableStopNextBreakpointAll :: IO ()
|
|
404 | + |
|
400 | 405 | foreign import ccall "&rts_stop_on_exception" exceptionFlag :: Ptr CInt
|
401 | 406 | |
407 | +-- | Enables the single step mode for all threads, thus stopping at any
|
|
408 | +-- existing breakpoint.
|
|
402 | 409 | setStepFlag :: IO ()
|
403 | -setStepFlag = poke stepFlag 1
|
|
410 | +setStepFlag = rts_enableStopNextBreakpointAll
|
|
411 | + |
|
404 | 412 | resetStepFlag :: IO ()
|
405 | -resetStepFlag = poke stepFlag 0
|
|
413 | +resetStepFlag = rts_disableStopNextBreakpointAll
|
|
406 | 414 | |
407 | 415 | type BreakpointCallback
|
408 | 416 | = Addr# -- pointer to the breakpoint tick module name
|
... | ... | @@ -243,9 +243,25 @@ 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 | +// A global toggle for single-step mode.
|
|
247 | +// Unlike `TSO_STOP_NEXT_BREAKPOINT`, which sets single-step mode per-thread,
|
|
248 | +// `rts_stop_next_breakpoint` globally enables single-step mode. If enabled, we
|
|
249 | +// will stop at the immediate next breakpoint regardless of what thread it is in.
|
|
246 | 250 | int rts_stop_next_breakpoint = 0;
|
247 | 251 | int rts_stop_on_exception = 0;
|
248 | 252 | |
253 | +// Enable "TSO_STOP_NEXT_BREAKPOINT" on all TSOs
|
|
254 | +void rts_enableStopNextBreakpointAll()
|
|
255 | +{
|
|
256 | + rts_stop_next_breakpoint = 1;
|
|
257 | +}
|
|
258 | + |
|
259 | +// Disable "TSO_STOP_NEXT_BREAKPOINT" on all TSOs
|
|
260 | +void rts_disableStopNextBreakpointAll()
|
|
261 | +{
|
|
262 | + rts_stop_next_breakpoint = 0;
|
|
263 | +}
|
|
264 | + |
|
249 | 265 | #if defined(INTERP_STATS)
|
250 | 266 | |
251 | 267 | #define N_CODES 128
|
... | ... | @@ -1250,7 +1266,7 @@ run_BCO: |
1250 | 1266 | int arg8_cc;
|
1251 | 1267 | #endif
|
1252 | 1268 | StgArrBytes *breakPoints;
|
1253 | - int returning_from_break;
|
|
1269 | + int returning_from_break, stop_next_breakpoint;
|
|
1254 | 1270 | |
1255 | 1271 | // the io action to run at a breakpoint
|
1256 | 1272 | StgClosure *ioAction;
|
... | ... | @@ -1280,6 +1296,13 @@ run_BCO: |
1280 | 1296 | returning_from_break =
|
1281 | 1297 | cap->r.rCurrentTSO->flags & TSO_STOPPED_ON_BREAKPOINT;
|
1282 | 1298 | |
1299 | + // check whether this thread is set to stop at the immediate next
|
|
1300 | + // breakpoint -- either by the global `rts_stop_next_breakpoint`
|
|
1301 | + // flag, or by the local `TSO_STOP_NEXT_BREAKPOINT`
|
|
1302 | + stop_next_breakpoint =
|
|
1303 | + rts_stop_next_breakpoint ||
|
|
1304 | + cap->r.rCurrentTSO->flags & TSO_STOP_NEXT_BREAKPOINT;
|
|
1305 | + |
|
1283 | 1306 | #if defined(PROFILING)
|
1284 | 1307 | cap->r.rCCCS = pushCostCentre(cap->r.rCCCS,
|
1285 | 1308 | (CostCentre*)BCO_LIT(arg8_cc));
|
... | ... | @@ -1290,21 +1313,23 @@ run_BCO: |
1290 | 1313 | if (!returning_from_break)
|
1291 | 1314 | {
|
1292 | 1315 | breakPoints = (StgArrBytes *) BCO_PTR(arg1_brk_array);
|
1316 | +
|
|
1293 | 1317 | |
1294 | 1318 | // stop the current thread if either the
|
1295 | - // "rts_stop_next_breakpoint" flag is true OR if the
|
|
1319 | + // "TSO_STOP_NEXT_BREAKPOINT" flag is true OR if the
|
|
1296 | 1320 | // ignore count for this particular breakpoint is zero
|
1297 | 1321 | StgInt ignore_count = ((StgInt*)breakPoints->payload)[arg6_tick_index];
|
1298 | - if (rts_stop_next_breakpoint == false && ignore_count > 0)
|
|
1322 | + if (stop_next_breakpoint == false && ignore_count > 0)
|
|
1299 | 1323 | {
|
1300 | 1324 | // decrement and write back ignore count
|
1301 | 1325 | ((StgInt*)breakPoints->payload)[arg6_tick_index] = --ignore_count;
|
1302 | 1326 | }
|
1303 | - else if (rts_stop_next_breakpoint == true || ignore_count == 0)
|
|
1327 | + else if (stop_next_breakpoint == true || ignore_count == 0)
|
|
1304 | 1328 | {
|
1305 | 1329 | // make sure we don't automatically stop at the
|
1306 | 1330 | // next breakpoint
|
1307 | - rts_stop_next_breakpoint = false;
|
|
1331 | + rts_stop_next_breakpoint = 0;
|
|
1332 | + cap->r.rCurrentTSO->flags &= ~TSO_STOP_NEXT_BREAKPOINT;
|
|
1308 | 1333 | |
1309 | 1334 | // allocate memory for a new AP_STACK, enough to
|
1310 | 1335 | // store the top stack frame plus an
|
... | ... | @@ -906,7 +906,8 @@ 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) \
|
|
910 | 911 | SymI_NeedsDataProto(rts_stop_on_exception) \
|
911 | 912 | SymI_HasProto(stopTimer) \
|
912 | 913 | 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
|