Hannes Siebenhandl pushed to branch wip/fendor/hpc-bc-support at Glasgow Haskell Compiler / GHC
Commits:
-
467794d8
by fendor at 2026-04-02T10:58:54+02:00
29 changed files:
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Driver/Backend.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Coverage.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/Types/HpcInfo.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- + libraries/ghci/GHCi/Coverage.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Disassembler.c
- rts/Hpc.c
- rts/Interpreter.c
- rts/include/rts/Bytecodes.h
- + testsuite/tests/hpc/ghc_ghci/BytecodeMain.hs
- testsuite/tests/hpc/ghc_ghci/Makefile
- + testsuite/tests/hpc/ghc_ghci/hpc_ghc_ghci_bytecode.stdout
- testsuite/tests/hpc/ghc_ghci/test.T
Changes:
| ... | ... | @@ -72,6 +72,7 @@ import GHC.Float (castFloatToWord32, castDoubleToWord64) |
| 72 | 72 | |
| 73 | 73 | import qualified Data.List as List ( any )
|
| 74 | 74 | import GHC.Exts
|
| 75 | +import qualified GHC.Data.Strict as Strict
|
|
| 75 | 76 | |
| 76 | 77 | |
| 77 | 78 | -- -----------------------------------------------------------------------------
|
| ... | ... | @@ -111,8 +112,9 @@ assembleBCOs |
| 111 | 112 | -> [(Name, ByteString)]
|
| 112 | 113 | -> Maybe InternalModBreaks
|
| 113 | 114 | -> [SptEntry]
|
| 115 | + -> Strict.Maybe ByteCodeHpcInfo
|
|
| 114 | 116 | -> IO CompiledByteCode
|
| 115 | -assembleBCOs profile proto_bcos tycons top_strs modbreaks spt_entries = do
|
|
| 117 | +assembleBCOs profile proto_bcos tycons top_strs modbreaks spt_entries use_hpc = do
|
|
| 116 | 118 | -- TODO: the profile should be bundled with the interpreter: the rts ways are
|
| 117 | 119 | -- fixed for an interpreter
|
| 118 | 120 | let itbls = mkITbls profile tycons
|
| ... | ... | @@ -123,6 +125,7 @@ assembleBCOs profile proto_bcos tycons top_strs modbreaks spt_entries = do |
| 123 | 125 | , bc_strs = top_strs
|
| 124 | 126 | , bc_breaks = modbreaks
|
| 125 | 127 | , bc_spt_entries = spt_entries
|
| 128 | + , bc_hpc_info = use_hpc
|
|
| 126 | 129 | }
|
| 127 | 130 | |
| 128 | 131 | -- Note [Allocating string literals]
|
| ... | ... | @@ -856,6 +859,12 @@ assembleI platform i = case i of |
| 856 | 859 | emit_ bci_BRK_FUN [ Op p1, Op info_addr, Op info_unitid_addr
|
| 857 | 860 | , SmallOp ix_hi, SmallOp ix_lo, Op np ]
|
| 858 | 861 | |
| 862 | + HPC_TICK lbl ix -> do
|
|
| 863 | + p <- lit1 (BCONPtrLbl lbl)
|
|
| 864 | + let ix_hi = fromIntegral (ix `shiftR` 16)
|
|
| 865 | + ix_lo = fromIntegral (ix .&. 0xffff)
|
|
| 866 | + emit_ bci_HPC_TICK [Op p, SmallOp ix_hi, SmallOp ix_lo]
|
|
| 867 | + |
|
| 859 | 868 | #if MIN_VERSION_rts(1,0,3)
|
| 860 | 869 | BCO_NAME name -> do np <- lit1 (BCONPtrStr name)
|
| 861 | 870 | emit_ bci_BCO_NAME [Op np]
|
| ... | ... | @@ -15,6 +15,7 @@ import GHC.ByteCode.Types |
| 15 | 15 | import GHC.Cmm.Type (Width)
|
| 16 | 16 | import GHC.StgToCmm.Layout ( ArgRep(..) )
|
| 17 | 17 | import GHC.Utils.Outputable
|
| 18 | +import GHC.Data.FastString ( FastString )
|
|
| 18 | 19 | import GHC.Types.Name
|
| 19 | 20 | import GHC.Types.Literal
|
| 20 | 21 | import GHC.Types.Unique
|
| ... | ... | @@ -257,6 +258,7 @@ data BCInstr |
| 257 | 258 | |
| 258 | 259 | -- Breakpoints
|
| 259 | 260 | | BRK_FUN !InternalBreakpointId
|
| 261 | + | HPC_TICK !FastString !Word32
|
|
| 260 | 262 | |
| 261 | 263 | #if MIN_VERSION_rts(1,0,3)
|
| 262 | 264 | -- | A "meta"-instruction for recording the name of a BCO for debugging purposes.
|
| ... | ... | @@ -452,6 +454,7 @@ instance Outputable BCInstr where |
| 452 | 454 | = text "BRK_FUN" <+> text "<breakarray>"
|
| 453 | 455 | <+> ppr info_mod <+> ppr infox
|
| 454 | 456 | <+> text "<cc>"
|
| 457 | + ppr (HPC_TICK lbl ix) = text "HPC_TICK" <+> ppr lbl <+> ppr ix
|
|
| 455 | 458 | #if MIN_VERSION_rts(1,0,3)
|
| 456 | 459 | ppr (BCO_NAME nm) = text "BCO_NAME" <+> text (show nm)
|
| 457 | 460 | #endif
|
| ... | ... | @@ -578,6 +581,7 @@ bciStackUse OP_INDEX_ADDR{} = 0 |
| 578 | 581 | |
| 579 | 582 | bciStackUse SWIZZLE{} = 0
|
| 580 | 583 | bciStackUse BRK_FUN{} = 0
|
| 584 | +bciStackUse HPC_TICK{} = 0
|
|
| 581 | 585 | |
| 582 | 586 | -- These insns actually reduce stack use, but we need the high-tide level,
|
| 583 | 587 | -- so can't use this info. Not that it matters much.
|
| ... | ... | @@ -25,6 +25,9 @@ module GHC.ByteCode.Types |
| 25 | 25 | -- * Mod Breaks
|
| 26 | 26 | , ModBreaks (..), BreakpointId(..), BreakTickIndex
|
| 27 | 27 | |
| 28 | + -- * Hpc Info
|
|
| 29 | + , ByteCodeHpcInfo(..)
|
|
| 30 | + |
|
| 28 | 31 | -- * Internal Mod Breaks
|
| 29 | 32 | , InternalModBreaks(..), CgBreakInfo(..), seqInternalModBreaks
|
| 30 | 33 | -- ** Internal breakpoint identifier
|
| ... | ... | @@ -35,6 +38,7 @@ import GHC.Prelude |
| 35 | 38 | |
| 36 | 39 | import GHC.Data.FastString
|
| 37 | 40 | import GHC.Data.FlatBag
|
| 41 | +import qualified GHC.Data.Strict as Strict
|
|
| 38 | 42 | import GHC.Types.Name
|
| 39 | 43 | import GHC.Types.Name.Env
|
| 40 | 44 | import GHC.Utils.Binary
|
| ... | ... | @@ -79,6 +83,25 @@ data CompiledByteCode = CompiledByteCode |
| 79 | 83 | -- ^ Static pointer table entries which should be loaded along with the
|
| 80 | 84 | -- BCOs. See Note [Grand plan for static forms] in
|
| 81 | 85 | -- "GHC.Iface.Tidy.StaticPtrTable".
|
| 86 | + |
|
| 87 | + , bc_hpc_info :: !(Strict.Maybe ByteCodeHpcInfo)
|
|
| 88 | + -- ^ 'ByteCodeHpcInfo' that should be added to the run-time system when this 'CompiledByteCode'
|
|
| 89 | + -- object is loaded.
|
|
| 90 | + --
|
|
| 91 | + -- It is safe to load the same 'ByteCodeHpcInfo' multiple times.
|
|
| 92 | + }
|
|
| 93 | + |
|
| 94 | +-- | ByteCode specific HPC information.
|
|
| 95 | +--
|
|
| 96 | +data ByteCodeHpcInfo = ByteCodeHpcInfo
|
|
| 97 | + { bchi_module_name :: !String
|
|
| 98 | + -- ^ Name of the module.
|
|
| 99 | + , bchi_tickbox_name :: !String
|
|
| 100 | + -- ^ Name of the tick box that has been added via 'CStub'.
|
|
| 101 | + , bchi_tick_count :: {-# UNPACK #-} !Int
|
|
| 102 | + -- ^ Number of ticks.
|
|
| 103 | + , bchi_hash :: {-# UNPACK #-} !Int
|
|
| 104 | + -- ^ mix-file hash.
|
|
| 82 | 105 | }
|
| 83 | 106 | |
| 84 | 107 | -- | A libffi ffi_cif function prototype.
|
| ... | ... | @@ -712,8 +712,7 @@ backendSupportsHpc (Named NCG) = True |
| 712 | 712 | backendSupportsHpc (Named LLVM) = True
|
| 713 | 713 | backendSupportsHpc (Named ViaC) = True
|
| 714 | 714 | backendSupportsHpc (Named JavaScript) = False
|
| 715 | --- TODO: @terrorjack thinks that the bytecode backend should support HPC now since (!13493)
|
|
| 716 | -backendSupportsHpc (Named Bytecode) = False
|
|
| 715 | +backendSupportsHpc (Named Bytecode) = True
|
|
| 717 | 716 | backendSupportsHpc (Named NoBackend) = True
|
| 718 | 717 | |
| 719 | 718 | -- | This flag says whether the back end supports foreign
|
| ... | ... | @@ -343,7 +343,6 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs |
| 343 | 343 | cplusplus_hdr = "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n"
|
| 344 | 344 | cplusplus_ftr = "#if defined(__cplusplus)\n}\n#endif\n"
|
| 345 | 345 | |
| 346 | - |
|
| 347 | 346 | -- It is more than likely that the stubs file will
|
| 348 | 347 | -- turn out to be empty, in which case no file should be created.
|
| 349 | 348 | outputForeignStubs_help :: FilePath -> String -> String -> String -> IO Bool
|
| ... | ... | @@ -134,6 +134,7 @@ import GHC.Driver.Config.Diagnostic |
| 134 | 134 | import GHC.Driver.Config.Tidy
|
| 135 | 135 | import GHC.Driver.Hooks
|
| 136 | 136 | import GHC.Driver.GenerateCgIPEStub (generateCgIPEStub, lookupEstimatedTicks)
|
| 137 | +import GHC.Driver.Ppr (showSDoc)
|
|
| 137 | 138 | |
| 138 | 139 | import GHC.Runtime.Context
|
| 139 | 140 | import GHC.Runtime.Interpreter
|
| ... | ... | @@ -151,6 +152,7 @@ import GHC.Hs.Dump |
| 151 | 152 | import GHC.Hs.Stats ( ppSourceStats )
|
| 152 | 153 | |
| 153 | 154 | import GHC.HsToCore
|
| 155 | +import GHC.HsToCore.Coverage ( hpcTickBoxes, hpcModuleName )
|
|
| 154 | 156 | |
| 155 | 157 | import GHC.StgToByteCode ( byteCodeGen )
|
| 156 | 158 | import GHC.StgToJS ( stgToJS )
|
| ... | ... | @@ -207,6 +209,8 @@ import qualified GHC.StgToCmm as StgToCmm ( codeGen ) |
| 207 | 209 | import GHC.StgToCmm.Types (CmmCgInfos (..), ModuleLFInfos, LambdaFormInfo(..))
|
| 208 | 210 | import GHC.StgToCmm.CgUtils (CgStream)
|
| 209 | 211 | |
| 212 | +import qualified GHC.ByteCode.Serialize as ByteCode
|
|
| 213 | + |
|
| 210 | 214 | import GHC.Cmm
|
| 211 | 215 | import GHC.Cmm.Info.Build
|
| 212 | 216 | import GHC.Cmm.Pipeline
|
| ... | ... | @@ -237,6 +241,7 @@ import GHC.Types.Var.Set |
| 237 | 241 | import GHC.Types.Error
|
| 238 | 242 | import GHC.Types.Fixity.Env
|
| 239 | 243 | import GHC.Types.CostCentre
|
| 244 | +import GHC.Types.HpcInfo (HpcInfo (..))
|
|
| 240 | 245 | import GHC.Types.IPE
|
| 241 | 246 | import GHC.Types.SourceFile
|
| 242 | 247 | import GHC.Types.SrcLoc
|
| ... | ... | @@ -260,6 +265,7 @@ import GHC.Utils.Touch |
| 260 | 265 | import GHC.Data.FastString
|
| 261 | 266 | import GHC.Data.Bag
|
| 262 | 267 | import GHC.Data.OsPath (unsafeEncodeUtf)
|
| 268 | +import qualified GHC.Data.Strict as Strict
|
|
| 263 | 269 | import GHC.Data.StringBuffer
|
| 264 | 270 | import qualified GHC.Data.Stream as Stream
|
| 265 | 271 | import GHC.Data.Maybe
|
| ... | ... | @@ -297,7 +303,6 @@ import GHC.Cmm.Config (CmmConfig) |
| 297 | 303 | import Data.Bifunctor
|
| 298 | 304 | import qualified GHC.Unit.Home.Graph as HUG
|
| 299 | 305 | import GHC.Unit.Home.PackageTable
|
| 300 | -import qualified GHC.ByteCode.Serialize as ByteCode
|
|
| 301 | 306 | |
| 302 | 307 | {- **********************************************************************
|
| 303 | 308 | %* *
|
| ... | ... | @@ -1185,7 +1190,7 @@ compileWholeCoreBindings hsc_env type_env wcb = do |
| 1185 | 1190 | gen_bytecode core_binds stubs foreign_files = do
|
| 1186 | 1191 | let cgi_guts = CgInteractiveGuts wcb_module core_binds
|
| 1187 | 1192 | (typeEnvTyCons type_env) stubs foreign_files
|
| 1188 | - Nothing []
|
|
| 1193 | + Nothing [] NoHpcInfo
|
|
| 1189 | 1194 | trace_if logger (text "Generating ByteCode for" <+> ppr wcb_module)
|
| 1190 | 1195 | mkModuleByteCode hsc_env wcb_module wcb_mod_location cgi_guts
|
| 1191 | 1196 | |
| ... | ... | @@ -2135,11 +2140,12 @@ data CgInteractiveGuts = CgInteractiveGuts { cgi_module :: Module |
| 2135 | 2140 | , cgi_foreign_files :: [(ForeignSrcLang, FilePath)]
|
| 2136 | 2141 | , cgi_modBreaks :: Maybe ModBreaks
|
| 2137 | 2142 | , cgi_spt_entries :: [SptEntry]
|
| 2143 | + , cgi_hpc_info :: HpcInfo
|
|
| 2138 | 2144 | }
|
| 2139 | 2145 | |
| 2140 | 2146 | mkCgInteractiveGuts :: CgGuts -> CgInteractiveGuts
|
| 2141 | -mkCgInteractiveGuts CgGuts{cg_module, cg_binds, cg_tycons, cg_foreign, cg_foreign_files, cg_modBreaks, cg_spt_entries}
|
|
| 2142 | - = CgInteractiveGuts cg_module cg_binds cg_tycons cg_foreign cg_foreign_files cg_modBreaks cg_spt_entries
|
|
| 2147 | +mkCgInteractiveGuts CgGuts{cg_module, cg_binds, cg_tycons, cg_foreign, cg_foreign_files, cg_modBreaks, cg_spt_entries, cg_hpc_info}
|
|
| 2148 | + = CgInteractiveGuts cg_module cg_binds cg_tycons cg_foreign cg_foreign_files cg_modBreaks cg_spt_entries cg_hpc_info
|
|
| 2143 | 2149 | |
| 2144 | 2150 | hscInteractive :: HscEnv
|
| 2145 | 2151 | -> CgInteractiveGuts
|
| ... | ... | @@ -2162,13 +2168,15 @@ hscGenerateByteCode :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO Compiled |
| 2162 | 2168 | hscGenerateByteCode hsc_env cgguts location = do
|
| 2163 | 2169 | let dflags = hsc_dflags hsc_env
|
| 2164 | 2170 | let logger = hsc_logger hsc_env
|
| 2171 | + let platform = targetPlatform dflags
|
|
| 2165 | 2172 | let CgInteractiveGuts{ -- This is the last use of the ModGuts in a compilation.
|
| 2166 | 2173 | -- From now on, we just use the bits we need.
|
| 2167 | 2174 | cgi_module = this_mod,
|
| 2168 | 2175 | cgi_binds = core_binds,
|
| 2169 | 2176 | cgi_tycons = tycons,
|
| 2170 | 2177 | cgi_modBreaks = mod_breaks,
|
| 2171 | - cgi_spt_entries = spt_entries } = cgguts
|
|
| 2178 | + cgi_spt_entries = spt_entries,
|
|
| 2179 | + cgi_hpc_info = hpc_info } = cgguts
|
|
| 2172 | 2180 | |
| 2173 | 2181 | -------------------
|
| 2174 | 2182 | -- ADD IMPLICIT BINDINGS
|
| ... | ... | @@ -2193,8 +2201,22 @@ hscGenerateByteCode hsc_env cgguts location = do |
| 2193 | 2201 | |
| 2194 | 2202 | let (stg_binds,_stg_deps) = unzip stg_binds_with_deps
|
| 2195 | 2203 | |
| 2204 | + -------------------
|
|
| 2205 | + -- Setup HPC info
|
|
| 2206 | + let
|
|
| 2207 | + -- Strict to not retain a reference to the 'cgguts' via 'hpc_info'
|
|
| 2208 | + !bytecodeHpcInfo = case hpc_info of
|
|
| 2209 | + NoHpcInfo -> Strict.Nothing
|
|
| 2210 | + HpcInfo{hpcInfoTickCount, hpcInfoHash} ->
|
|
| 2211 | + Strict.Just ByteCodeHpcInfo
|
|
| 2212 | + { bchi_tick_count = hpcInfoTickCount
|
|
| 2213 | + , bchi_hash = hpcInfoHash
|
|
| 2214 | + , bchi_tickboxes = showSDoc dflags $ hpcTickBoxes platform this_mod
|
|
| 2215 | + , bchi_module_name = showSDoc dflags $ hpcModuleName this_mod
|
|
| 2216 | + }
|
|
| 2217 | + |
|
| 2196 | 2218 | ----------------- Generate byte code ------------------
|
| 2197 | - byteCodeGen hsc_env this_mod stg_binds tycons mod_breaks spt_entries
|
|
| 2219 | + byteCodeGen hsc_env this_mod stg_binds tycons mod_breaks spt_entries bytecodeHpcInfo
|
|
| 2198 | 2220 | |
| 2199 | 2221 | -- | Generate a byte code object linkable and write it to a file if `-fwrite-byte-code` is enabled.
|
| 2200 | 2222 | generateAndWriteByteCode :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO ModuleByteCode
|
| ... | ... | @@ -2843,6 +2865,7 @@ hscCompileCoreExpr' hsc_env srcspan ds_expr = do |
| 2843 | 2865 | []
|
| 2844 | 2866 | Nothing -- modbreaks
|
| 2845 | 2867 | [] -- spt entries
|
| 2868 | + Strict.Nothing -- no hpc info
|
|
| 2846 | 2869 | |
| 2847 | 2870 | {- load it -}
|
| 2848 | 2871 | bco_time <- getCurrentTime
|
| ... | ... | @@ -3791,12 +3791,6 @@ makeDynFlagsConsistent dflags |
| 3791 | 3791 | pgmError (backendDescription (backend dflags) ++
|
| 3792 | 3792 | " supports only unregisterised ABI but target platform doesn't use it.")
|
| 3793 | 3793 | |
| 3794 | - | gopt Opt_Hpc dflags && not (backendSupportsHpc (backend dflags))
|
|
| 3795 | - = let dflags' = gopt_unset dflags Opt_Hpc
|
|
| 3796 | - warn = "Hpc can't be used with " ++ backendDescription (backend dflags) ++
|
|
| 3797 | - ". Ignoring -fhpc."
|
|
| 3798 | - in loop dflags' warn
|
|
| 3799 | - |
|
| 3800 | 3794 | | backendSwappableWithViaC (backend dflags) &&
|
| 3801 | 3795 | platformUnregisterised (targetPlatform dflags)
|
| 3802 | 3796 | = loop (dflags { backend = viaCBackend })
|
| ... | ... | @@ -163,20 +163,20 @@ deSugar hsc_env |
| 163 | 163 | export_set (typeEnvTyCons type_env) binds
|
| 164 | 164 | else return (binds, Nothing)
|
| 165 | 165 | ; let modBreaks
|
| 166 | - | Just (_, specs) <- m_tickInfo
|
|
| 166 | + | Just (_, _, breakpointSpecs) <- m_tickInfo
|
|
| 167 | 167 | , breakpointsAllowed dflags
|
| 168 | - = Just $ mkModBreaks (interpreterProfiled $ hscInterp hsc_env) mod specs
|
|
| 168 | + = Just $ mkModBreaks (interpreterProfiled $ hscInterp hsc_env) mod breakpointSpecs
|
|
| 169 | 169 | | otherwise
|
| 170 | 170 | = Nothing
|
| 171 | 171 | |
| 172 | 172 | ; ds_hpc_info <- case m_tickInfo of
|
| 173 | - Just (orig_file2, ticks)
|
|
| 173 | + Just (orig_file2, hpcTicks, _)
|
|
| 174 | 174 | | gopt Opt_Hpc $ hsc_dflags hsc_env
|
| 175 | 175 | -> do
|
| 176 | 176 | hashNo <- if gopt Opt_Hpc $ hsc_dflags hsc_env
|
| 177 | - then writeMixEntries (hpcDir dflags) mod ticks orig_file2
|
|
| 177 | + then writeMixEntries (hpcDir dflags) mod hpcTicks orig_file2
|
|
| 178 | 178 | else return 0 -- dummy hash when none are written
|
| 179 | - pure $ HpcInfo (fromIntegral $ sizeSS ticks) hashNo
|
|
| 179 | + pure $ HpcInfo (fromIntegral $ sizeSS hpcTicks) hashNo
|
|
| 180 | 180 | _ -> pure $ emptyHpcInfo
|
| 181 | 181 | |
| 182 | 182 | ; (msgs, mb_res) <- initDs hsc_env tcg_env $
|
| ... | ... | @@ -6,6 +6,9 @@ |
| 6 | 6 | module GHC.HsToCore.Coverage
|
| 7 | 7 | ( writeMixEntries
|
| 8 | 8 | , hpcInitCode
|
| 9 | + , hpcStubLabel
|
|
| 10 | + , hpcModuleName
|
|
| 11 | + , hpcTickBoxes
|
|
| 9 | 12 | ) where
|
| 10 | 13 | |
| 11 | 14 | import GHC.Prelude as Prelude
|
| ... | ... | @@ -116,24 +119,33 @@ hpcInitCode _ _ (NoHpcInfo {}) = mempty |
| 116 | 119 | hpcInitCode platform this_mod (HpcInfo tickCount hashNo)
|
| 117 | 120 | = initializerCStub platform fn_name decls body
|
| 118 | 121 | where
|
| 119 | - fn_name = mkInitializerStubLabel this_mod (fsLit "hpc")
|
|
| 122 | + fn_name = hpcStubLabel this_mod
|
|
| 120 | 123 | decls = text "StgWord64 " <> tickboxes <> brackets (int tickCount) <> semi
|
| 121 | 124 | body = text "hs_hpc_module" <>
|
| 122 | 125 | parens (hcat (punctuate comma [
|
| 123 | - doubleQuotes full_name_str,
|
|
| 126 | + doubleQuotes (hpcModuleName this_mod),
|
|
| 124 | 127 | int tickCount, -- really StgWord32
|
| 125 | 128 | int hashNo, -- really StgWord32
|
| 126 | 129 | tickboxes
|
| 127 | 130 | ])) <> semi
|
| 131 | + tickboxes = hpcTickBoxes platform this_mod
|
|
| 128 | 132 | |
| 129 | - tickboxes = pprCLabel platform (mkHpcTicksLabel $ this_mod)
|
|
| 130 | - |
|
| 131 | - module_name = hcat (map (text.charToC) $ BS.unpack $
|
|
| 132 | - bytesFS (moduleNameFS (moduleName this_mod)))
|
|
| 133 | - package_name = hcat (map (text.charToC) $ BS.unpack $
|
|
| 134 | - bytesFS (unitFS (moduleUnit this_mod)))
|
|
| 135 | - full_name_str
|
|
| 136 | - | moduleUnit this_mod == mainUnit
|
|
| 137 | - = module_name
|
|
| 138 | - | otherwise
|
|
| 139 | - = package_name <> char '/' <> module_name |
|
| 133 | +hpcStubLabel :: Module -> CLabel
|
|
| 134 | +hpcStubLabel this_mod = mkInitializerStubLabel this_mod (fsLit "hpc")
|
|
| 135 | + |
|
| 136 | +hpcModuleName :: Module -> SDoc
|
|
| 137 | +hpcModuleName this_mod = full_name_str
|
|
| 138 | + where
|
|
| 139 | + full_name_str
|
|
| 140 | + | moduleUnit this_mod == mainUnit
|
|
| 141 | + = module_name
|
|
| 142 | + | otherwise
|
|
| 143 | + = package_name <> char '/' <> module_name
|
|
| 144 | + module_name = hcat (map (text.charToC) $ BS.unpack $
|
|
| 145 | + bytesFS (moduleNameFS (moduleName this_mod)))
|
|
| 146 | + |
|
| 147 | + package_name = hcat (map (text.charToC) $ BS.unpack $
|
|
| 148 | + bytesFS (unitFS (moduleUnit this_mod)))
|
|
| 149 | + |
|
| 150 | +hpcTickBoxes :: Platform -> Module -> SDoc
|
|
| 151 | +hpcTickBoxes platform this_mod = pprCLabel platform (mkHpcTicksLabel this_mod) |
| ... | ... | @@ -100,7 +100,7 @@ addTicksToBinds |
| 100 | 100 | -- hasn't set it), so we have to work from this set.
|
| 101 | 101 | -> [TyCon] -- ^ Type constructors in this module
|
| 102 | 102 | -> LHsBinds GhcTc
|
| 103 | - -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick))
|
|
| 103 | + -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
|
|
| 104 | 104 | |
| 105 | 105 | addTicksToBinds logger cfg
|
| 106 | 106 | mod mod_loc exports tyCons binds
|
| ... | ... | @@ -133,12 +133,13 @@ addTicksToBinds logger cfg |
| 133 | 133 | |
| 134 | 134 | (binds1,st) = foldr tickPass (binds, initTTState) passes
|
| 135 | 135 | |
| 136 | - extendedMixEntries = ticks st
|
|
| 136 | + hpcEntries = hpcTicks st
|
|
| 137 | + breakpointEntries = breakpointTicks st
|
|
| 137 | 138 | |
| 138 | 139 | putDumpFileMaybe logger Opt_D_dump_ticked "HPC" FormatHaskell
|
| 139 | 140 | (pprLHsBinds binds1)
|
| 140 | 141 | |
| 141 | - return (binds1, Just (orig_file2, extendedMixEntries))
|
|
| 142 | + return (binds1, Just (orig_file2, hpcEntries, breakpointEntries))
|
|
| 142 | 143 | |
| 143 | 144 | | otherwise = return (binds, Nothing)
|
| 144 | 145 | |
| ... | ... | @@ -1050,23 +1051,31 @@ addTickArithSeqInfo (FromThenTo e1 e2 e3) = |
| 1050 | 1051 | (addTickLHsExpr e2)
|
| 1051 | 1052 | (addTickLHsExpr e3)
|
| 1052 | 1053 | |
| 1053 | -data TickTransState = TT { ticks :: !(SizedSeq Tick)
|
|
| 1054 | - , ccIndices :: !CostCentreState
|
|
| 1055 | - , recSelTicks :: !(IdEnv CoreTickish)
|
|
| 1054 | +data TickTransState = TT { hpcTicks :: !(SizedSeq Tick)
|
|
| 1055 | + , breakpointTicks :: !(SizedSeq Tick)
|
|
| 1056 | + , ccIndices :: !CostCentreState
|
|
| 1057 | + , recSelTicks :: !(IdEnv CoreTickish)
|
|
| 1056 | 1058 | }
|
| 1057 | 1059 | |
| 1058 | 1060 | initTTState :: TickTransState
|
| 1059 | -initTTState = TT { ticks = emptySS
|
|
| 1060 | - , ccIndices = newCostCentreState
|
|
| 1061 | - , recSelTicks = emptyVarEnv
|
|
| 1061 | +initTTState = TT { hpcTicks = emptySS
|
|
| 1062 | + , breakpointTicks = emptySS
|
|
| 1063 | + , ccIndices = newCostCentreState
|
|
| 1064 | + , recSelTicks = emptyVarEnv
|
|
| 1062 | 1065 | }
|
| 1063 | 1066 | |
| 1064 | -addMixEntry :: Tick -> TM Int
|
|
| 1065 | -addMixEntry ent = do
|
|
| 1066 | - c <- fromIntegral . sizeSS . ticks <$> getState
|
|
| 1067 | +addHpcEntry :: Tick -> TM Int
|
|
| 1068 | +addHpcEntry ent = do
|
|
| 1069 | + c <- fromIntegral . sizeSS . hpcTicks <$> getState
|
|
| 1067 | 1070 | setState $ \st ->
|
| 1068 | - st { ticks = addToSS (ticks st) ent
|
|
| 1069 | - }
|
|
| 1071 | + st { hpcTicks = addToSS (hpcTicks st) ent }
|
|
| 1072 | + return c
|
|
| 1073 | + |
|
| 1074 | +addBreakpointEntry :: Tick -> TM Int
|
|
| 1075 | +addBreakpointEntry ent = do
|
|
| 1076 | + c <- fromIntegral . sizeSS . breakpointTicks <$> getState
|
|
| 1077 | + setState $ \st ->
|
|
| 1078 | + st { breakpointTicks = addToSS (breakpointTicks st) ent }
|
|
| 1070 | 1079 | return c
|
| 1071 | 1080 | |
| 1072 | 1081 | addRecSelTick :: Id -> CoreTickish -> TM ()
|
| ... | ... | @@ -1291,7 +1300,7 @@ mkTickish boxLabel countEntries topOnly pos fvs decl_path = do |
| 1291 | 1300 | |
| 1292 | 1301 | env <- getEnv
|
| 1293 | 1302 | case tickishType env of
|
| 1294 | - HpcTicks -> HpcTick (this_mod env) <$> addMixEntry me
|
|
| 1303 | + HpcTicks -> HpcTick (this_mod env) <$> addHpcEntry me
|
|
| 1295 | 1304 | |
| 1296 | 1305 | ProfNotes -> do
|
| 1297 | 1306 | flavour <- mkHpcCCFlavour <$> getCCIndexM cc_name
|
| ... | ... | @@ -1300,7 +1309,7 @@ mkTickish boxLabel countEntries topOnly pos fvs decl_path = do |
| 1300 | 1309 | return $ ProfNote cc count True{-scopes-}
|
| 1301 | 1310 | |
| 1302 | 1311 | Breakpoints -> do
|
| 1303 | - i <- addMixEntry me
|
|
| 1312 | + i <- addBreakpointEntry me
|
|
| 1304 | 1313 | pure (Breakpoint noExtField (BreakpointId (this_mod env) i) ids)
|
| 1305 | 1314 | |
| 1306 | 1315 | SourceNotes | RealSrcSpan pos' _ <- pos ->
|
| ... | ... | @@ -1325,19 +1334,19 @@ mkBinTickBoxHpc :: (Bool -> BoxLabel) -> SrcSpan -> LHsExpr GhcTc |
| 1325 | 1334 | mkBinTickBoxHpc boxLabel pos e = do
|
| 1326 | 1335 | env <- getEnv
|
| 1327 | 1336 | binTick <- HsBinTick
|
| 1328 | - <$> addMixEntry (Tick { tick_loc = pos
|
|
| 1337 | + <$> addHpcEntry (Tick { tick_loc = pos
|
|
| 1329 | 1338 | , tick_path = declPath env
|
| 1330 | 1339 | , tick_ids = []
|
| 1331 | 1340 | , tick_label = boxLabel True
|
| 1332 | 1341 | })
|
| 1333 | - <*> addMixEntry (Tick { tick_loc = pos
|
|
| 1342 | + <*> addHpcEntry (Tick { tick_loc = pos
|
|
| 1334 | 1343 | , tick_path = declPath env
|
| 1335 | 1344 | , tick_ids = []
|
| 1336 | 1345 | , tick_label = boxLabel False
|
| 1337 | 1346 | })
|
| 1338 | 1347 | <*> pure e
|
| 1339 | 1348 | tick <- HpcTick (this_mod env)
|
| 1340 | - <$> addMixEntry (Tick { tick_loc = pos
|
|
| 1349 | + <$> addHpcEntry (Tick { tick_loc = pos
|
|
| 1341 | 1350 | , tick_path = declPath env
|
| 1342 | 1351 | , tick_ids = []
|
| 1343 | 1352 | , tick_label = ExpBox False
|
| ... | ... | @@ -402,6 +402,7 @@ tidyProgram opts (ModGuts { mg_module = mod |
| 402 | 402 | , mg_foreign_files = foreign_files
|
| 403 | 403 | , mg_modBreaks = modBreaks
|
| 404 | 404 | , mg_boot_exports = boot_exports
|
| 405 | + , mg_hpc_info = hpc_info
|
|
| 405 | 406 | }) = do
|
| 406 | 407 | |
| 407 | 408 | (unfold_env, tidy_occ_env) <- chooseExternalIds opts mod tcs binds imp_rules
|
| ... | ... | @@ -471,6 +472,7 @@ tidyProgram opts (ModGuts { mg_module = mod |
| 471 | 472 | , cg_dep_pkgs = S.map snd (dep_direct_pkgs deps)
|
| 472 | 473 | , cg_modBreaks = modBreaks
|
| 473 | 474 | , cg_spt_entries = spt_entries
|
| 475 | + , cg_hpc_info = hpc_info
|
|
| 474 | 476 | }
|
| 475 | 477 | , ModDetails { md_types = tidy_type_env
|
| 476 | 478 | , md_rules = tidy_rules
|
| ... | ... | @@ -137,6 +137,10 @@ import qualified Data.IntMap.Strict as IM |
| 137 | 137 | import qualified Data.Map.Strict as M
|
| 138 | 138 | import Foreign.Ptr (nullPtr)
|
| 139 | 139 | import GHC.ByteCode.Serialize
|
| 140 | +-- TODO: this import is wrong
|
|
| 141 | +import GHC.HsToCore.Coverage (hpcModuleName)
|
|
| 142 | +import qualified Data.ByteString.Char8 as BS8
|
|
| 143 | +import qualified GHC.Data.Strict as Strict
|
|
| 140 | 144 | |
| 141 | 145 | -- Note [Linkers and loaders]
|
| 142 | 146 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -983,10 +987,9 @@ dynLinkBCOs interp pls keep_spec bcos = |
| 983 | 987 | let (bcos_loaded', new_bcos) = rmDupLinkables (bcos_loaded pls) bcos
|
| 984 | 988 | pls1 = pls { bcos_loaded = bcos_loaded' }
|
| 985 | 989 | |
| 986 | - cbcs :: [CompiledByteCode]
|
|
| 987 | - cbcs = concatMap linkableBCOs new_bcos
|
|
| 990 | + mbcs = concatMap linkableBCOs new_bcos
|
|
| 988 | 991 | in do
|
| 989 | - bco_state <- dynLinkCompiledByteCode interp (pkgs_loaded pls) (bco_loader_state pls) traverseHomePackageBytecodeState keep_spec cbcs
|
|
| 992 | + bco_state <- dynLinkCompiledByteCode interp (pkgs_loaded pls) (bco_loader_state pls) traverseHomePackageBytecodeState keep_spec mbcs
|
|
| 990 | 993 | return $! pls1 { bco_loader_state = bco_state }
|
| 991 | 994 | |
| 992 | 995 | dynLinkCompiledByteCode :: Interp
|
| ... | ... | @@ -996,22 +999,22 @@ dynLinkCompiledByteCode :: Interp |
| 996 | 999 | -> KeepModuleLinkableDefinitions
|
| 997 | 1000 | -> [CompiledByteCode]
|
| 998 | 1001 | -> IO BytecodeLoaderState
|
| 999 | -dynLinkCompiledByteCode interp pkgs_loaded whole_bytecode_state traverse_bytecode_state keep_spec cbcs = do
|
|
| 1002 | +dynLinkCompiledByteCode interp pkgs_loaded whole_bytecode_state traverse_bytecode_state keep_spec mbcs = do
|
|
| 1000 | 1003 | st1 <- traverse_bytecode_state whole_bytecode_state $ \bytecode_state -> do
|
| 1001 | 1004 | let
|
| 1002 | 1005 | le1 = bco_linker_env bytecode_state
|
| 1003 | 1006 | lb1 = bco_linked_breaks bytecode_state
|
| 1004 | - ie2 <- linkITbls interp (itbl_env le1) (concatMap bc_itbls cbcs)
|
|
| 1005 | - ae2 <- foldlM (\env cbc -> allocateTopStrings interp (bc_strs cbc) env) (addr_env le1) cbcs
|
|
| 1006 | - be2 <- allocateBreakArrays interp (breakarray_env lb1) (catMaybes $ map bc_breaks cbcs)
|
|
| 1007 | - ce2 <- allocateCCS interp (ccs_env lb1) (catMaybes $ map bc_breaks cbcs)
|
|
| 1007 | + ie2 <- linkITbls interp (itbl_env le1) (concatMap bc_itbls mbcs)
|
|
| 1008 | + ae2 <- foldlM (\env cbc -> allocateTopStrings interp (bc_strs cbc) env) (addr_env le1) mbcs
|
|
| 1009 | + be2 <- allocateBreakArrays interp (breakarray_env lb1) (catMaybes $ map bc_breaks mbcs)
|
|
| 1010 | + ce2 <- allocateCCS interp (ccs_env lb1) (catMaybes $ map bc_breaks mbcs)
|
|
| 1008 | 1011 | let le2 = le1 { itbl_env = ie2, addr_env = ae2 }
|
| 1009 | 1012 | let lb2 = lb1 { breakarray_env = be2, ccs_env = ce2 }
|
| 1010 | 1013 | return $! bytecode_state { bco_linker_env = le2, bco_linked_breaks = lb2 }
|
| 1011 | 1014 | |
| 1012 | 1015 | -- NB: Important to pass the whole bytecode loader state to linkSomeBCOs so that you can find Names in local
|
| 1013 | 1016 | -- and external packages.
|
| 1014 | - names_and_refs <- linkSomeBCOs interp pkgs_loaded st1 cbcs
|
|
| 1017 | + names_and_refs <- linkSomeBCOs interp pkgs_loaded st1 mbcs
|
|
| 1015 | 1018 | |
| 1016 | 1019 | -- We only want to add the external ones to the ClosureEnv
|
| 1017 | 1020 | let (to_add, to_drop) = partition (keepDefinitions keep_spec . fst) names_and_refs
|
| ... | ... | @@ -1024,7 +1027,9 @@ dynLinkCompiledByteCode interp pkgs_loaded whole_bytecode_state traverse_bytecod |
| 1024 | 1027 | traverse_bytecode_state st1 $ \bytecode_state -> do
|
| 1025 | 1028 | let ce2 = extendClosureEnv (closure_env (bco_linker_env bytecode_state)) new_binds
|
| 1026 | 1029 | -- Add SPT entries
|
| 1027 | - mapM_ (linkSptEntry interp ce2) (concatMap bc_spt_entries cbcs)
|
|
| 1030 | + mapM_ (linkSptEntry interp ce2) (concatMap bc_spt_entries mbcs)
|
|
| 1031 | + -- Load HPC modules
|
|
| 1032 | + mapM_ (linkHpcEntry interp . bc_hpc_info) mbcs
|
|
| 1028 | 1033 | return $! bytecode_state { bco_linker_env = (bco_linker_env bytecode_state) { closure_env = ce2 } }
|
| 1029 | 1034 | |
| 1030 | 1035 | -- | Register SPT entries for this module in the interpreter
|
| ... | ... | @@ -1037,8 +1042,14 @@ linkSptEntry interp ce (SptEntry name fpr) = do |
| 1037 | 1042 | Nothing -> pprPanic "linkSptEntry" (ppr name)
|
| 1038 | 1043 | Just (_, hval) -> addSptEntry interp fpr hval
|
| 1039 | 1044 | |
| 1040 | - |
|
| 1041 | - |
|
| 1045 | +linkHpcEntry :: Interp -> Strict.Maybe ByteCodeHpcInfo -> IO ()
|
|
| 1046 | +linkHpcEntry _interp Strict.Nothing = pure ()
|
|
| 1047 | +linkHpcEntry interp (Strict.Just info) = do
|
|
| 1048 | + addHpcModule interp
|
|
| 1049 | + (bchi_module_name info)
|
|
| 1050 | + (bchi_tick_count info)
|
|
| 1051 | + (bchi_hash info)
|
|
| 1052 | + (bchi_tickboxes info)
|
|
| 1042 | 1053 | |
| 1043 | 1054 | -- Link a bunch of BCOs and return references to their values
|
| 1044 | 1055 | linkSomeBCOs :: Interp
|
| ... | ... | @@ -214,36 +214,37 @@ data BytecodeLoaderState = BytecodeLoaderState |
| 214 | 214 | -- ^ Information about bytecode objects from the home package we have loaded into the interpreter.
|
| 215 | 215 | , externalPackage_loaded :: BytecodeState
|
| 216 | 216 | -- ^ Information about bytecode objects from external packages we have loaded into the interpreter.
|
| 217 | + , hpcInitialised :: !Bool
|
|
| 217 | 218 | }
|
| 218 | 219 | |
| 219 | 220 | |
| 220 | 221 | -- | Find a name loaded from bytecode
|
| 221 | 222 | lookupNameBytecodeState :: BytecodeLoaderState -> Name -> Maybe (Name, ForeignHValue)
|
| 222 | -lookupNameBytecodeState (BytecodeLoaderState home_package external_package) name = do
|
|
| 223 | +lookupNameBytecodeState (BytecodeLoaderState home_package external_package _) name = do
|
|
| 223 | 224 | lookupNameEnv (closure_env (bco_linker_env home_package)) name
|
| 224 | 225 | <|> lookupNameEnv (closure_env (bco_linker_env external_package)) name
|
| 225 | 226 | |
| 226 | 227 | -- | Look up a break array in the bytecode loader state.
|
| 227 | 228 | lookupBreakArrayBytecodeState :: BytecodeLoaderState -> Module -> Maybe (ForeignRef BreakArray)
|
| 228 | -lookupBreakArrayBytecodeState (BytecodeLoaderState home_package external_package) break_mod = do
|
|
| 229 | +lookupBreakArrayBytecodeState (BytecodeLoaderState home_package external_package _) break_mod = do
|
|
| 229 | 230 | lookupModuleEnv (breakarray_env (bco_linked_breaks home_package)) break_mod
|
| 230 | 231 | <|> lookupModuleEnv (breakarray_env (bco_linked_breaks external_package)) break_mod
|
| 231 | 232 | |
| 232 | 233 | -- | Look up an info table in the bytecode loader state.
|
| 233 | 234 | lookupInfoTableBytecodeState :: BytecodeLoaderState -> Name -> Maybe (Name, ItblPtr)
|
| 234 | -lookupInfoTableBytecodeState (BytecodeLoaderState home_package external_package) info_mod = do
|
|
| 235 | +lookupInfoTableBytecodeState (BytecodeLoaderState home_package external_package _) info_mod = do
|
|
| 235 | 236 | lookupNameEnv (itbl_env (bco_linker_env home_package)) info_mod
|
| 236 | 237 | <|> lookupNameEnv (itbl_env (bco_linker_env external_package)) info_mod
|
| 237 | 238 | |
| 238 | 239 | -- | Look up an address in the bytecode loader state.
|
| 239 | 240 | lookupAddressBytecodeState :: BytecodeLoaderState -> Name -> Maybe (Name, AddrPtr)
|
| 240 | -lookupAddressBytecodeState (BytecodeLoaderState home_package external_package) addr_mod = do
|
|
| 241 | +lookupAddressBytecodeState (BytecodeLoaderState home_package external_package _) addr_mod = do
|
|
| 241 | 242 | lookupNameEnv (addr_env (bco_linker_env home_package)) addr_mod
|
| 242 | 243 | <|> lookupNameEnv (addr_env (bco_linker_env external_package)) addr_mod
|
| 243 | 244 | |
| 244 | 245 | -- | Look up a cost centre stack in the bytecode loader state.
|
| 245 | 246 | lookupCCSBytecodeState :: BytecodeLoaderState -> Module -> Maybe (Array BreakTickIndex (RemotePtr CostCentre))
|
| 246 | -lookupCCSBytecodeState (BytecodeLoaderState home_package external_package) ccs_mod = do
|
|
| 247 | +lookupCCSBytecodeState (BytecodeLoaderState home_package external_package _) ccs_mod = do
|
|
| 247 | 248 | lookupModuleEnv (ccs_env (bco_linked_breaks home_package)) ccs_mod
|
| 248 | 249 | <|> lookupModuleEnv (ccs_env (bco_linked_breaks external_package)) ccs_mod
|
| 249 | 250 | |
| ... | ... | @@ -251,6 +252,7 @@ emptyBytecodeLoaderState :: BytecodeLoaderState |
| 251 | 252 | emptyBytecodeLoaderState = BytecodeLoaderState
|
| 252 | 253 | { homePackage_loaded = emptyBytecodeState
|
| 253 | 254 | , externalPackage_loaded = emptyBytecodeState
|
| 255 | + , hpcInitialised = False
|
|
| 254 | 256 | }
|
| 255 | 257 | |
| 256 | 258 | emptyBytecodeState :: BytecodeState
|
| ... | ... | @@ -17,6 +17,7 @@ module GHC.Runtime.Interpreter |
| 17 | 17 | , mallocData
|
| 18 | 18 | , createBCOs
|
| 19 | 19 | , addSptEntry
|
| 20 | + , addHpcModule
|
|
| 20 | 21 | , mkCostCentres
|
| 21 | 22 | , costCentreStackInfo
|
| 22 | 23 | , newBreakArray
|
| ... | ... | @@ -366,6 +367,10 @@ addSptEntry interp fpr ref = |
| 366 | 367 | withForeignRef ref $ \val ->
|
| 367 | 368 | interpCmd interp (AddSptEntry fpr val)
|
| 368 | 369 | |
| 370 | +addHpcModule :: Interp -> String -> Int -> Int -> String -> IO ()
|
|
| 371 | +addHpcModule interp modLabel tickNo hash tickboxes =
|
|
| 372 | + interpCmd interp (AddHpcModule modLabel tickNo hash tickboxes)
|
|
| 373 | + |
|
| 369 | 374 | costCentreStackInfo :: Interp -> RemotePtr CostCentreStack -> IO [String]
|
| 370 | 375 | costCentreStackInfo interp ccs =
|
| 371 | 376 | interpCmd interp (CostCentreStackInfo ccs)
|
| ... | ... | @@ -20,6 +20,7 @@ import GHC.ByteCode.Types |
| 20 | 20 | |
| 21 | 21 | import GHC.Cmm.CallConv
|
| 22 | 22 | import GHC.Cmm.Expr
|
| 23 | +import GHC.Cmm.CLabel (mkHpcTicksLabel, pprCLabel)
|
|
| 23 | 24 | import GHC.Cmm.Reg ( GlobalArgRegs(..) )
|
| 24 | 25 | import GHC.Cmm.Node
|
| 25 | 26 | import GHC.Cmm.Utils
|
| ... | ... | @@ -97,6 +98,7 @@ import Control.Monad.IO.Class |
| 97 | 98 | import Control.Monad.Trans.Reader (ReaderT(..))
|
| 98 | 99 | import Control.Monad.Trans.State (StateT(..))
|
| 99 | 100 | import Data.Bifunctor (Bifunctor(..))
|
| 101 | +import qualified GHC.Data.Strict as Strict
|
|
| 100 | 102 | |
| 101 | 103 | -- -----------------------------------------------------------------------------
|
| 102 | 104 | -- Generating byte code for a complete module
|
| ... | ... | @@ -107,8 +109,9 @@ byteCodeGen :: HscEnv |
| 107 | 109 | -> [TyCon]
|
| 108 | 110 | -> Maybe ModBreaks
|
| 109 | 111 | -> [SptEntry]
|
| 112 | + -> Strict.Maybe ByteCodeHpcInfo
|
|
| 110 | 113 | -> IO CompiledByteCode
|
| 111 | -byteCodeGen hsc_env this_mod binds tycs mb_modBreaks spt_entries
|
|
| 114 | +byteCodeGen hsc_env this_mod binds tycs mb_modBreaks spt_entries hpc_info
|
|
| 112 | 115 | = withTiming logger
|
| 113 | 116 | (text "GHC.StgToByteCode"<+>brackets (ppr this_mod))
|
| 114 | 117 | (const ()) $ do
|
| ... | ... | @@ -134,7 +137,7 @@ byteCodeGen hsc_env this_mod binds tycs mb_modBreaks spt_entries |
| 134 | 137 | let mod_breaks = case mb_modBreaks of
|
| 135 | 138 | Nothing -> Nothing
|
| 136 | 139 | Just mb -> Just $ mkInternalModBreaks this_mod breakInfo mb
|
| 137 | - cbc <- assembleBCOs profile proto_bcos tycs strings mod_breaks spt_entries
|
|
| 140 | + cbc <- assembleBCOs profile proto_bcos tycs strings mod_breaks spt_entries hpc_info
|
|
| 138 | 141 | |
| 139 | 142 | -- Squash space leaks in the CompiledByteCode. This is really
|
| 140 | 143 | -- important, because when loading a set of modules into GHCi
|
| ... | ... | @@ -604,6 +607,11 @@ schemeE _d _s _p (StgTick (Breakpoint _ bp_id _) _rhs) |
| 604 | 607 | = pprPanic "schemeE: Breakpoint without let binding:"
|
| 605 | 608 | (ppr bp_id <+> text "forgot to run bcPrep?")
|
| 606 | 609 | |
| 610 | +schemeE d s p (StgTick (HpcTick mod ix) rhs) = do
|
|
| 611 | + platform <- profilePlatform <$> getProfile
|
|
| 612 | + rhs_code <- schemeE d s p rhs
|
|
| 613 | + pure (unitOL (HPC_TICK (mkHpcTickLabel platform mod) (fromIntegral ix)) `appOL` rhs_code)
|
|
| 614 | + |
|
| 607 | 615 | -- ignore other kinds of tick
|
| 608 | 616 | schemeE d s p (StgTick _ rhs) = schemeE d s p rhs
|
| 609 | 617 | |
| ... | ... | @@ -2784,6 +2792,10 @@ getLastBreakTick = BcM $ \env st -> |
| 2784 | 2792 | tickFS :: FastString
|
| 2785 | 2793 | tickFS = fsLit "ticked"
|
| 2786 | 2794 | |
| 2795 | +mkHpcTickLabel :: Platform -> Module -> FastString
|
|
| 2796 | +mkHpcTickLabel platform mod =
|
|
| 2797 | + fsLit (showSDocOneLine defaultSDocContext (pprCLabel platform (mkHpcTicksLabel mod)))
|
|
| 2798 | + |
|
| 2787 | 2799 | -- Dehydrating CgBreakInfo
|
| 2788 | 2800 | |
| 2789 | 2801 | dehydrateCgBreakInfo :: [TyVar] -> [Maybe (Id, Word)] -> Type -> Either InternalBreakLoc BreakpointId -> CgBreakInfo
|
| ... | ... | @@ -18,4 +18,3 @@ data HpcInfo |
| 18 | 18 | |
| 19 | 19 | emptyHpcInfo :: HpcInfo
|
| 20 | 20 | emptyHpcInfo = NoHpcInfo |
| 21 | - |
| ... | ... | @@ -141,8 +141,9 @@ data CgGuts |
| 141 | 141 | cg_dep_pkgs :: !(Set UnitId), -- ^ Dependent packages, used to
|
| 142 | 142 | -- generate #includes for C code gen
|
| 143 | 143 | cg_modBreaks :: !(Maybe ModBreaks), -- ^ Module breakpoints
|
| 144 | - cg_spt_entries :: [SptEntry]
|
|
| 144 | + cg_spt_entries :: [SptEntry],
|
|
| 145 | 145 | -- ^ Static pointer table entries for static forms defined in
|
| 146 | 146 | -- the module.
|
| 147 | 147 | -- See Note [Grand plan for static forms] in "GHC.Iface.Tidy.StaticPtrTable"
|
| 148 | + cg_hpc_info :: HpcInfo
|
|
| 148 | 149 | } |
| 1 | +{-# LANGUAGE ForeignFunctionInterface #-}
|
|
| 2 | +{-# LANGUAGE LambdaCase #-}
|
|
| 3 | + |
|
| 4 | +module GHCi.Coverage (
|
|
| 5 | + hpcAddModule,
|
|
| 6 | + ) where
|
|
| 7 | + |
|
| 8 | +import Prelude -- See note [Why do we import Prelude here?]
|
|
| 9 | + |
|
| 10 | +import Control.Exception
|
|
| 11 | +import qualified Data.ByteString.Char8 as BS8
|
|
| 12 | +import qualified Data.ByteString.Unsafe as B
|
|
| 13 | +import Data.Word
|
|
| 14 | +import Foreign
|
|
| 15 | +import Foreign.C.String (withCAString)
|
|
| 16 | +import GHC.Fingerprint
|
|
| 17 | +import GHC.Foreign (CString)
|
|
| 18 | +import GHCi.ObjLink (lookupSymbol)
|
|
| 19 | + |
|
| 20 | +-- | Inform the run-time system that the given module name is instrumented via @hpc@
|
|
| 21 | +-- and to collect @.tix@ info.
|
|
| 22 | +--
|
|
| 23 | +-- Starts the `hpc` run-time if it hasn't already been started.
|
|
| 24 | +hpcAddModule ::
|
|
| 25 | + String ->
|
|
| 26 | + -- ^ Name of the module to instrument
|
|
| 27 | + Int ->
|
|
| 28 | + -- ^ Number of hpc ticks in this module
|
|
| 29 | + Int ->
|
|
| 30 | + -- ^ 'HpcInfo's 'hpcInfoHash'
|
|
| 31 | + String ->
|
|
| 32 | + -- ^ Name of the ticks array found in the c-stub.
|
|
| 33 | + IO ()
|
|
| 34 | +hpcAddModule modlName ticks hash tickboxes = do
|
|
| 35 | + withCAString modlName $ \modlNameLiteral -> do
|
|
| 36 | + -- we need to find the reference to the ticks array.
|
|
| 37 | + lookupSymbol tickboxes >>= \ case
|
|
| 38 | + Nothing -> do
|
|
| 39 | + -- the symbol is not found, this is a bug!
|
|
| 40 | + throwIO $ ErrorCall $ "hpcAddModule: failed to find symbol " <> tickboxes
|
|
| 41 | + Just tickBoxRef -> do
|
|
| 42 | + -- Calling 'hs_hpc_module' multiple times is safe, it will add the module only once.
|
|
| 43 | + hpc_register_module modlNameLiteral (fromIntegral ticks) (fromIntegral hash) (castPtr tickBoxRef)
|
|
| 44 | + -- calling 'hpc_startup' multiple times is safe, it will only be initialised once.
|
|
| 45 | + hpc_startup
|
|
| 46 | + |
|
| 47 | +foreign import ccall "hs_hpc_module"
|
|
| 48 | + hpc_register_module :: CString -> Word32 -> Word32 -> Ptr Word64 -> IO ()
|
|
| 49 | + |
|
| 50 | +foreign import ccall "startupHpc"
|
|
| 51 | + hpc_startup :: IO () |
| ... | ... | @@ -111,6 +111,8 @@ data Message a where |
| 111 | 111 | |
| 112 | 112 | -- | Add entries to the Static Pointer Table
|
| 113 | 113 | AddSptEntry :: Fingerprint -> HValueRef -> Message ()
|
| 114 | + -- | Add module to hpc
|
|
| 115 | + AddHpcModule :: String -> Int -> Int -> String -> Message ()
|
|
| 114 | 116 | |
| 115 | 117 | -- | Malloc some data and return a 'RemotePtr' to it
|
| 116 | 118 | MallocData :: ByteString -> Message (RemotePtr ())
|
| ... | ... | @@ -607,7 +609,8 @@ getMessage = do |
| 607 | 609 | 38 -> Msg <$> (ResumeSeq <$> get)
|
| 608 | 610 | 39 -> Msg <$> (LookupSymbolInDLL <$> get <*> get)
|
| 609 | 611 | 40 -> Msg <$> (WhereFrom <$> get)
|
| 610 | - 41 -> Msg <$> (CustomMessage <$> get <*> get)
|
|
| 612 | + 41 -> Msg <$> (AddHpcModule <$> get <*> get <*> get <*> get)
|
|
| 613 | + 42 -> Msg <$> (CustomMessage <$> get <*> get)
|
|
| 611 | 614 | _ -> error $ "Unknown Message code " ++ (show b)
|
| 612 | 615 | |
| 613 | 616 | putMessage :: Message a -> Put
|
| ... | ... | @@ -654,7 +657,8 @@ putMessage m = case m of |
| 654 | 657 | ResumeSeq a -> putWord8 38 >> put a
|
| 655 | 658 | LookupSymbolInDLL dll str -> putWord8 39 >> put dll >> put str
|
| 656 | 659 | WhereFrom a -> putWord8 40 >> put a
|
| 657 | - CustomMessage tag payload -> putWord8 41 >> put tag >> put payload
|
|
| 660 | + AddHpcModule m n h ticks -> putWord8 41 >> put m >> put n >> put h >> put ticks
|
|
| 661 | + CustomMessage tag payload -> putWord8 42 >> put tag >> put payload
|
|
| 658 | 662 | |
| 659 | 663 | {-
|
| 660 | 664 | Note [Parallelize CreateBCOs serialization]
|
| ... | ... | @@ -19,6 +19,7 @@ import GHCi.CreateBCO |
| 19 | 19 | import GHCi.InfoTable
|
| 20 | 20 | #endif
|
| 21 | 21 | |
| 22 | +import GHCi.Coverage
|
|
| 22 | 23 | import qualified GHC.InfoProv as InfoProv
|
| 23 | 24 | import GHCi.Debugger
|
| 24 | 25 | import GHCi.FFI
|
| ... | ... | @@ -88,6 +89,7 @@ run m = case m of |
| 88 | 89 | fmap toRemotePtr <$> lookupSymbolInDLL (fromRemotePtr dll) str
|
| 89 | 90 | FreeHValueRefs rs -> mapM_ freeRemoteRef rs
|
| 90 | 91 | AddSptEntry fpr r -> localRef r >>= sptAddEntry fpr
|
| 92 | + AddHpcModule modl ticks hash tickboxes -> hpcAddModule modl ticks hash tickboxes
|
|
| 91 | 93 | EvalStmt opts r -> evalStmt opts r
|
| 92 | 94 | ResumeStmt opts r -> resumeStmt opts r
|
| 93 | 95 | AbandonStmt r -> abandonStmt r
|
| ... | ... | @@ -59,6 +59,7 @@ library |
| 59 | 59 | if flag(internal-interpreter)
|
| 60 | 60 | CPP-Options: -DHAVE_INTERNAL_INTERPRETER
|
| 61 | 61 | exposed-modules:
|
| 62 | + GHCi.Coverage
|
|
| 62 | 63 | GHCi.Run
|
| 63 | 64 | GHCi.Debugger
|
| 64 | 65 | GHCi.CreateBCO
|
| ... | ... | @@ -101,6 +101,13 @@ disInstr ( StgBCO *bco, int pc ) |
| 101 | 101 | }
|
| 102 | 102 | debugBelch("\n");
|
| 103 | 103 | break; }
|
| 104 | + case bci_HPC_TICK: {
|
|
| 105 | + W_ p1, info_wix;
|
|
| 106 | + p1 = BCO_GET_LARGE_ARG;
|
|
| 107 | + info_wix = BCO_READ_NEXT_32;
|
|
| 108 | + debugBelch("HPC_TICK "); printPtr((StgPtr)literals[p1]);
|
|
| 109 | + debugBelch(" %" FMT_Word "\n", info_wix);
|
|
| 110 | + break; }
|
|
| 104 | 111 | case bci_SWIZZLE: {
|
| 105 | 112 | W_ stkoff = BCO_GET_LARGE_ARG;
|
| 106 | 113 | StgInt by = BCO_GET_LARGE_ARG;
|
| ... | ... | @@ -270,6 +270,9 @@ hs_hpc_module(char *modName, |
| 270 | 270 | HpcModuleInfo *tmpModule;
|
| 271 | 271 | uint32_t i;
|
| 272 | 272 | |
| 273 | + debugTrace(DEBUG_hpc, "hs_hpc_module(%s, count=%u, hash=%u)\n",
|
|
| 274 | + modName, modCount, modHashNo);
|
|
| 275 | + |
|
| 273 | 276 | if (moduleHash == NULL) {
|
| 274 | 277 | moduleHash = allocStrHashTable();
|
| 275 | 278 | }
|
| ... | ... | @@ -1711,7 +1711,7 @@ run_BCO: |
| 1711 | 1711 | &&lbl_bci_TESTEQ_W8 - &&lbl_bci_DEFAULT,
|
| 1712 | 1712 | &&lbl_bci_PRIMCALL - &&lbl_bci_DEFAULT,
|
| 1713 | 1713 | &&lbl_bci_BCO_NAME - &&lbl_bci_DEFAULT,
|
| 1714 | - &&lbl_bci_DEFAULT - &&lbl_bci_DEFAULT,
|
|
| 1714 | + &&lbl_bci_HPC_TICK - &&lbl_bci_DEFAULT,
|
|
| 1715 | 1715 | &&lbl_bci_OP_ADD_64 - &&lbl_bci_DEFAULT,
|
| 1716 | 1716 | &&lbl_bci_OP_SUB_64 - &&lbl_bci_DEFAULT,
|
| 1717 | 1717 | &&lbl_bci_OP_AND_64 - &&lbl_bci_DEFAULT,
|
| ... | ... | @@ -2078,6 +2078,18 @@ run_BCO: |
| 2078 | 2078 | NEXT_INSTRUCTION;
|
| 2079 | 2079 | }
|
| 2080 | 2080 | |
| 2081 | + INSTRUCTION(bci_HPC_TICK): {
|
|
| 2082 | + W_ arg1_ticks_array, arg2_tick_index;
|
|
| 2083 | + arg1_ticks_array = BCO_GET_LARGE_ARG;
|
|
| 2084 | + arg2_tick_index = BCO_READ_NEXT_32;
|
|
| 2085 | + IF_DEBUG(hpc,
|
|
| 2086 | + debugBelch("\tHPC Tick %lu %lu %lu\n", BCO_LIT(arg1_ticks_array), arg1_ticks_array, arg2_tick_index);
|
|
| 2087 | + );
|
|
| 2088 | + |
|
| 2089 | + ((StgWord64*)BCO_LIT(arg1_ticks_array))[arg2_tick_index]++;
|
|
| 2090 | + NEXT_INSTRUCTION;
|
|
| 2091 | + }
|
|
| 2092 | + |
|
| 2081 | 2093 | INSTRUCTION(bci_STKCHECK): {
|
| 2082 | 2094 | // Explicit stack check at the beginning of a function
|
| 2083 | 2095 | // *only* (stack checks in case alternatives are
|
| ... | ... | @@ -118,6 +118,7 @@ |
| 118 | 118 | #define bci_PRIMCALL 87
|
| 119 | 119 | |
| 120 | 120 | #define bci_BCO_NAME 88
|
| 121 | +#define bci_HPC_TICK 89
|
|
| 121 | 122 | |
| 122 | 123 | #define bci_OP_ADD_64 90
|
| 123 | 124 | #define bci_OP_SUB_64 91
|
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +inc :: Int -> Int
|
|
| 4 | +inc x = x + 1
|
|
| 5 | + |
|
| 6 | +double :: Int -> Int
|
|
| 7 | +double x = x * 2
|
|
| 8 | + |
|
| 9 | +main :: IO ()
|
|
| 10 | +main = print (double (inc 1011)) |
| ... | ... | @@ -7,3 +7,9 @@ hpc_ghc_ghci: |
| 7 | 7 | '$(TEST_HC)' $(TEST_HC_OPTS) -fhpc -c A.hs
|
| 8 | 8 | echo b | '$(TEST_HC)' $(TEST_HC_OPTS_INTERACTIVE) B.hs
|
| 9 | 9 | |
| 10 | +hpc_ghc_ghci_bytecode:
|
|
| 11 | + rm -f ./*.tix
|
|
| 12 | + printf "main\n:quit\n" | '$(TEST_HC)' $(TEST_HC_OPTS_INTERACTIVE) -fhpc -fbyte-code-and-object-code -fprefer-byte-code BytecodeMain.hs
|
|
| 13 | + @[ -f .hpc/Main.mix ] || (echo "ERROR: Expected .hpc/Main.mix file not found"; exit 1)
|
|
| 14 | + @set -- ./*.tix; [ -f "$$1" ] || (echo "ERROR: Expected .tix file not found"; exit 1); '$(HPC)' report "$$1" Main > hpc-report.txt
|
|
| 15 | + @grep -F "100% expressions used" hpc-report.txt >/dev/null || (echo "ERROR: Expected full expression coverage in hpc report"; cat hpc-report.txt; exit 1) |
| 1 | +2024 |
| ... | ... | @@ -3,3 +3,8 @@ test('hpc_ghc_ghci', |
| 3 | 3 | [extra_files(['A.hs', 'B.hs']),
|
| 4 | 4 | only_ways(['normal']), when(compiler_profiled(), skip), req_interp],
|
| 5 | 5 | run_command, ['$MAKE -s --no-print-directory hpc_ghc_ghci'])
|
| 6 | + |
|
| 7 | +test('hpc_ghc_ghci_bytecode',
|
|
| 8 | + [extra_files(['BytecodeMain.hs']),
|
|
| 9 | + only_ways(['normal']), when(compiler_profiled(), skip), req_interp],
|
|
| 10 | + run_command, ['$MAKE -s --no-print-directory hpc_ghc_ghci_bytecode']) |