Add support for `hpc` in the bytecode interpreter

Adds support to produce .tix from interpreted bytecode.

Conceptually, we insert HPC ticks into the bytecode similar to how we insert breakpoints. HPC and breakpoints do not share the same tick array or index.

Further, we teach the bytecode interpreter how to handle hpc ticks.

A tricky bit is "registering" a bytecode object for HPC instrumentation. In the compiled case, this is achieved via CStub and initializer/finalizers .init sections which are called when executable is run. See https://maskray.me/blog/2021-11-07-init-ctors-init-array for an explanation. After the initializers have been invoked, which is before hs_init_ghc, we then call startup_hpc in hs_init_ghc iff any modules were "registered" for hpc instrumentation via hs_hpc_module.

Since bytecode objects are loaded after starting up GHCi, this workflow doesn't work for supporting hpc.

We fix this issue by employing the same technique as is for SptEntrys:

  • We introduce a new field to CompiledByteCode, called ByteCodeHpcInfo which contains enough information to call hs_hpc_module, allowing us to register the module for hpc instrumentation`.
  • After registering the module, we unconditionally call startupHpc, to make sure the .tix file is written.

Calling startupHpc multiple times is safe. Calling hs_hpc_module multiple times for the same module is also safe.

A generalisation of this could be to call the initialisation functions of both SptEntry and Hpc that can be found in the CStub.

Closes #27036 (closed)

Merge request reports

Loading