RTS initializes tix datastructures with information from disk
My investigation into this issue started when I discovered that the examineTix
function from the module Trace.Hpc.Reflect behaves strangely. I always thought that the function has the sole functionality to make the current state of the tix datastructures from the RTS available in a Haskell program. But this appears not to be the case, consider for example the following program:
module Main where
import Trace.Hpc.Reflect (examineTix)
import Trace.Hpc.Tix (writeTix)
main :: IO ()
main = do
tix <- examineTix
print tix
If I run this program (compiled with ghc -fhpc Example.hs
) multiple times, then the output is increased every time:
david@anaxes ~/tmp $ ./Example
Tix [TixModule "Main" 1750432110 5 [1,0,0,1,1]]
david@anaxes ~/tmp $ ./Example
Tix [TixModule "Main" 1750432110 5 [2,1,1,2,2]]
david@anaxes ~/tmp $ ./Example
Tix [TixModule "Main" 1750432110 5 [3,2,2,3,3]]
david@anaxes ~/tmp $ ./Example
Tix [TixModule "Main" 1750432110 5 [4,3,3,4,4]]
david@anaxes ~/tmp $ ./Example
Tix [TixModule "Main" 1750432110 5 [5,4,4,5,5]]
david@anaxes ~/tmp $ ./Example
Tix [TixModule "Main" 1750432110 5 [6,5,5,6,6]]
david@anaxes ~/tmp $
Somehow the tix's are accumulated over multiple runs of the program, which is not at all what I expect.
The function examineTix
seems to have the side effect of reading in the Example.tix
file from disk and to populate the RTS internal data structure with the information from this file. I do not think this is the intended behaviour of examineTix
, but I guess I am currently the only user of that function, so I cannot tell what the intended behaviour originally was.
The following is my investigation into why this strange side-effect happens:
Investigation
The code responsible for the described behaviour lives in two files: libraries/hpc/Trace/Hpc/Reflect.hs
and rts/Hpc.c
Trace.Hpc.Reflect
The implementation of the examineTix
file looks harmless, and it looks like it should just inspect the RTS internal data structures:
foreign import ccall unsafe hs_hpc_rootModule :: IO (Ptr ())
modInfo :: [ModuleInfo]
modInfo = unsafePerformIO $ do
ptr <- hs_hpc_rootModule
moduleInfoList ptr
data ModuleInfo = ModuleInfo String Word32 Hash (Ptr Word64)
moduleInfoList :: Ptr () -> IO [ModuleInfo]
moduleInfoList ptr
| ptr == nullPtr = return []
| otherwise = do
cModName <- ((\hsc_ptr -> peekByteOff hsc_ptr 0)) ptr
{-# LINE 34 "Trace/Hpc/Reflect.hsc" #-}
modName <- peekCString cModName
tickCount <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) ptr
{-# LINE 36 "Trace/Hpc/Reflect.hsc" #-}
hashNo <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) ptr
{-# LINE 37 "Trace/Hpc/Reflect.hsc" #-}
tixArr <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) ptr
{-# LINE 38 "Trace/Hpc/Reflect.hsc" #-}
next <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) ptr
{-# LINE 39 "Trace/Hpc/Reflect.hsc" #-}
rest <- moduleInfoList next
return $ ModuleInfo modName tickCount (toHash (hashNo :: Int)) tixArr : rest
examineTix :: IO Tix
examineTix = do
mods <- sequence [ do tixs <- peekArray (fromIntegral count) ptr
return $ TixModule mod' hash (fromIntegral count)
$ map fromIntegral tixs
| (ModuleInfo mod' count hash ptr) <- modInfo
]
return $ Tix mods
Hpc.c
The implementation of hs_hpc_rootModule
is also completely innocent:
//////////////////////////////////////////////////////////////////////////////
// This is the API into Hpc RTS from Haskell, allowing the tixs boxes
// to be first class.
HpcModuleInfo *hs_hpc_rootModule(void) {
return modules;
}
So my conclusion is that the function examineTix
is correct, but that there is something strange going on with how the tix data structures are initialized during program startup.
There is a function readTix()
in the module which has the effect of populating the tix data structures with the information from the tix file on disk.
This function is called at the end of the function startupHpc()
.
Does anyone know why this is the case? Why does the RTS need to have the capability to read in .tix files in the first place. I would have guessed that it only needs to write those files.