Changing internal data structures used by Hpc
authorandy@galois.com <unknown>
Mon, 30 Apr 2007 22:59:15 +0000 (22:59 +0000)
committerandy@galois.com <unknown>
Mon, 30 Apr 2007 22:59:15 +0000 (22:59 +0000)
 - .tix files are now a list of MixModule, which contain a hash of the contents of the .mix file.
 - .mix files now have (the same) hash number.

This changes allow different binaries that use the same module compiled in the same way
to share coverage information.

compiler/codeGen/CgHpc.hs
compiler/deSugar/Coverage.lhs
compiler/main/HscTypes.lhs
includes/RtsExternal.h
rts/Hpc.c

index d5f3542..7bd7c03 100644 (file)
@@ -58,16 +58,16 @@ cgTickBox mod n = do
       visible_tick = mkFastString "hs_hpc_tick"
 
 hpcTable :: Module -> HpcInfo -> Code
-hpcTable this_mod hpc_tickCount = do
+hpcTable this_mod (HpcInfo hpc_tickCount _) = do
                         emitData ReadOnlyData
                                         [ CmmDataLabel mkHpcModuleNameLabel
                                         , CmmString $ map (fromIntegral . ord)
                                                          (module_name_str)
                                                       ++ [0]
                                         ]
-                        emitData Data
+                        emitData Data  -- change Offset => Data or Info
                                         [ CmmDataLabel (mkHpcModuleOffsetLabel this_mod)
-                                       , CmmStaticLit (CmmInt 0 I32)
+                                       , CmmStaticLit (CmmInt 0 I32)   -- stored offset?
                                         ]
                         emitData Data $ [ CmmDataLabel (mkHpcTicksLabel this_mod)
                                         ] ++
@@ -76,10 +76,10 @@ hpcTable this_mod hpc_tickCount = do
                                         ]
   where
     module_name_str = moduleNameString (Module.moduleName this_mod)
-
+hpcTable this_mod (NoHpcInfo) = error "TODO: impossible"
 
 initHpc :: Module -> HpcInfo -> Code
-initHpc this_mod tickCount
+initHpc this_mod (HpcInfo tickCount hashNo)
   = do { id <- newTemp wordRep
        ; emitForeignCall'
                PlayRisky
@@ -90,6 +90,7 @@ initHpc this_mod tickCount
                )
                [ (mkLblExpr mkHpcModuleNameLabel,PtrHint)
                , (CmmLit $ mkIntCLit tickCount,NoHint)
+               , (CmmLit $ mkIntCLit hashNo,NoHint)
                , (CmmLit $ CmmLabel $ mkHpcTicksLabel $ this_mod,PtrHint)
                ]
                (Just [])
index 4fe4fab..530e7d2 100644 (file)
@@ -22,6 +22,7 @@ import Var
 import VarSet
 import Data.List
 import FastString
+import HscTypes        
 import StaticFlags
 import UniqFM
 
@@ -35,10 +36,11 @@ import Compat.Directory ( createDirectoryIfMissing )
 import System.Directory ( createDirectoryIfMissing )
 #endif
 
-import HscTypes 
 import BreakArray 
+import Data.HashTable   ( hashString )
 \end{code}
 
+
 %************************************************************************
 %*                                                                     *
 %*             The main function: addCoverageTicksToBinds
@@ -51,7 +53,7 @@ addCoverageTicksToBinds
         -> Module
         -> ModLocation          -- of the current module
         -> LHsBinds Id
-        -> IO (LHsBinds Id, Int, ModBreaks)
+        -> IO (LHsBinds Id, HpcInfo, ModBreaks)
 
 addCoverageTicksToBinds dflags mod mod_loc binds = do 
   let orig_file = 
@@ -59,30 +61,39 @@ addCoverageTicksToBinds dflags mod mod_loc binds = do
                    Just file -> file
                    Nothing -> panic "can not find the original file during hpc trans"
 
-  if "boot" `isSuffixOf` orig_file then return (binds, 0, emptyModBreaks) else do
+  if "boot" `isSuffixOf` orig_file then return (binds, noHpcInfo, emptyModBreaks) else do
 
   let mod_name = moduleNameString (moduleName mod)
 
   let (binds1,_,st)
                 = unTM (addTickLHsBinds binds) 
-                  TT { modName      = mod_name
+                  (TTE
+                      { modName      = mod_name
                      , declPath     = []
-                     , tickBoxCount = 0
+                      })
+                  (TT 
+                     { tickBoxCount = 0
                      , mixEntries   = []
                       , inScope      = emptyVarSet
-                     }
+                     })
 
   let entries = reverse $ mixEntries st
 
   -- write the mix entries for this module
-  when opt_Hpc $ do
+  hashNo <- if opt_Hpc then do
      let hpc_dir = hpcDir dflags
      let tabStop = 1 -- <tab> counts as a normal char in GHC's location ranges.
      createDirectoryIfMissing True hpc_dir
      modTime <- getModificationTime' orig_file
      let entries' = [ (hpcPos, box) 
-                    | (span,_,box) <- entries, Just hpcPos <- [mkHpcPos span] ]
-     mixCreate hpc_dir mod_name (Mix orig_file modTime tabStop entries')
+                    | (span,_,box) <- entries, hpcPos <- [mkHpcPos span] ]
+     when (length entries' /= tickBoxCount st) $ do
+       panic "the number of .mix entries are inconsistent"
+     let hashNo = mixHash orig_file modTime tabStop entries'
+     mixCreate hpc_dir mod_name (Mix orig_file modTime hashNo tabStop entries')
+     return $ hashNo 
+   else do
+     return $ 0
 
   -- Todo: use proper src span type
   breakArray <- newBreakArray $ length entries
@@ -100,7 +111,7 @@ addCoverageTicksToBinds dflags mod mod_loc binds = do
   doIfSet_dyn dflags  Opt_D_dump_hpc $ do
          printDump (pprLHsBinds binds1)
 
-  return (binds1, tickBoxCount st, modBreaks)
+  return (binds1, HpcInfo (tickBoxCount st) hashNo, modBreaks)
 \end{code}
 
 
@@ -136,7 +147,7 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = (L _ id)  }))) = do
      else do
         tick_no <- allocATickBox (if null decl_path
                                      then TopLevelBox [name]
-                                     else LocalBox (name : decl_path)) 
+                                     else LocalBox (decl_path ++ [name])) 
                                 pos fvs
 
         return $ L pos $ funBind { fun_matches = MatchGroup matches' ty 
@@ -169,7 +180,7 @@ addTickLHsBind other = return other
 -- add a tick to the expression no matter what it is
 addTickLHsExprAlways :: LHsExpr Id -> TM (LHsExpr Id)
 addTickLHsExprAlways (L pos e0) = do
-    allocTickBox ExpBox pos $ addTickHsExpr e0
+    allocTickBox (ExpBox False) pos $ addTickHsExpr e0
 
 addTickLHsExprNeverOrAlways :: LHsExpr Id -> TM (LHsExpr Id)
 addTickLHsExprNeverOrAlways e
@@ -194,7 +205,7 @@ addTickLHsExpr :: LHsExpr Id -> TM (LHsExpr Id)
 addTickLHsExpr (L pos e0) = do
     if opt_Hpc || isGoodBreakExpr e0
        then do
-          allocTickBox ExpBox pos $ addTickHsExpr e0
+          allocTickBox (ExpBox False) pos $ addTickHsExpr e0
        else do
           e1 <- addTickHsExpr e0
           return $ L pos e1 
@@ -216,7 +227,7 @@ addTickLHsExprOptAlt :: Bool -> LHsExpr Id -> TM (LHsExpr Id)
 addTickLHsExprOptAlt oneOfMany (L pos e0)
   | not opt_Hpc = addTickLHsExpr (L pos e0)
   | otherwise =
-    allocTickBox (if oneOfMany then AltBox else ExpBox) pos $ 
+    allocTickBox (ExpBox oneOfMany) pos $ 
         addTickHsExpr e0
 
 addBinTickLHsExpr :: (Bool -> BoxLabel) -> LHsExpr Id -> TM (LHsExpr Id)
@@ -258,7 +269,7 @@ addTickHsExpr (HsCase e mgs) =
                (addTickMatchGroup mgs)
 addTickHsExpr (HsIf     e1 e2 e3) = 
        liftM3 HsIf
-               (addBinTickLHsExpr CondBinBox e1)
+               (addBinTickLHsExpr (BinBox CondBinBox) e1)
                (addTickLHsExprOptAlt True e2)
                (addTickLHsExprOptAlt True e3)
 addTickHsExpr (HsLet binds e) =
@@ -272,7 +283,7 @@ addTickHsExpr (HsDo cxt stmts last_exp srcloc) = do
        return (HsDo cxt stmts' last_exp' srcloc)
   where
        forQual = case cxt of
-                   ListComp -> Just QualBinBox
+                   ListComp -> Just $ BinBox QualBinBox
                    _        -> Nothing
 addTickHsExpr (ExplicitList ty es) = 
        liftM2 ExplicitList 
@@ -305,7 +316,7 @@ addTickHsExpr (ArithSeq      ty arith_seq) =
                (return ty)
                (addTickArithSeqInfo arith_seq)
 addTickHsExpr (HsTickPragma (file,(l1,c1),(l2,c2)) (L pos e0)) = do
-    e2 <- allocTickBox (ExternalBox (unpackFS file) (P l1 c1 l2 c2)) pos $
+    e2 <- allocTickBox (ExpBox False) pos $
                 addTickHsExpr e0
     return $ unLoc e2
 addTickHsExpr (PArrSeq  {}) = error "addTickHsExpr: PArrSeq"
@@ -366,7 +377,7 @@ addTickGRHSs isOneOfMany (GRHSs guarded local_binds) = do
 
 addTickGRHS :: Bool -> GRHS Id -> TM (GRHS Id)
 addTickGRHS isOneOfMany (GRHS stmts expr) = do
-  (stmts',expr') <- addTickLStmts' (Just $ GuardBinBox) stmts
+  (stmts',expr') <- addTickLStmts' (Just $ BinBox $ GuardBinBox) stmts
                         (if opt_Hpc then addTickLHsExprOptAlt isOneOfMany expr
                                     else addTickLHsExprAlways expr)
   return $ GRHS stmts' expr'
@@ -500,12 +511,15 @@ addTickArithSeqInfo (FromThenTo e1 e2 e3) =
 \end{code}
 
 \begin{code}
-data TickTransState = TT { modName     :: String
-                         , declPath    :: [String]
-                         , tickBoxCount:: Int
+data TickTransState = TT { tickBoxCount:: Int
                          , mixEntries  :: [MixEntry]
-                         , inScope     :: VarSet
+                         , inScope     :: VarSet       -- move the TickTransEnv
                          }                        
+
+data TickTransEnv = TTE { modName      :: String
+                       , declPath     :: [String]
+                       }
+
 --     deriving Show
 
 type FreeVars = OccEnv Id
@@ -523,56 +537,67 @@ noFVs = emptyOccEnv
 --   to filter additions to the latter.  This gives us complete control
 --   over what free variables we track.
 
-data TM a = TM { unTM :: TickTransState -> (a,FreeVars,TickTransState) }
+data TM a = TM { unTM :: TickTransEnv -> TickTransState -> (a,FreeVars,TickTransState) }
         -- a combination of a state monad (TickTransState) and a writer
         -- monad (FreeVars).
 
 instance Monad TM where
-  return a = TM $ \ st -> (a,noFVs,st)
-  (TM m) >>= k = TM $ \ st -> case m st of
+  return a = TM $ \ env st -> (a,noFVs,st)
+  (TM m) >>= k = TM $ \ env st -> 
+                               case m env st of
                                  (r1,fv1,st1) -> 
-                                     case unTM (k r1) st1 of
+                                     case unTM (k r1) env st1 of
                                        (r2,fv2,st2) -> 
                                           (r2, fv1 `plusOccEnv` fv2, st2)
 
 getState :: TM TickTransState
-getState = TM $ \st -> (st, noFVs, st)
+getState = TM $ \ env st -> (st, noFVs, st)
 
 setState :: (TickTransState -> TickTransState) -> TM ()
-setState f = TM $ \st -> ((), noFVs, f st)
+setState f = TM $ \ env st -> ((), noFVs, f st)
 
 withState :: (TickTransState -> TickTransState) -> TM a -> TM a
-withState f (TM m) = TM $ \st -> case m (f st) of
+withState f (TM m) = TM $ \ env st -> 
+                                case m env (f st) of
+                                   (a, fvs, st') -> (a, fvs, st')
+
+getEnv :: TM TickTransEnv
+getEnv = TM $ \ env st -> (env, noFVs, st)
+
+withEnv :: (TickTransEnv -> TickTransEnv) -> TM a -> TM a
+withEnv f (TM m) = TM $ \ env st -> 
+                                case m (f env) st of
                                    (a, fvs, st') -> (a, fvs, st')
 
 getFreeVars :: TM a -> TM (FreeVars, a)
 getFreeVars (TM m) 
-  = TM $ \st -> case m st of (a, fv, st') -> ((fv,a), fv, st')
+  = TM $ \ env st -> case m env st of (a, fv, st') -> ((fv,a), fv, st')
 
 freeVar :: Id -> TM ()
-freeVar id = TM $ \st -> 
+freeVar id = TM $ \ env st -> 
                 if id `elemVarSet` inScope st
                    then ((), unitOccEnv (nameOccName (idName id)) id, st)
                    else ((), noFVs, st)
 
 addPathEntry :: String -> TM a -> TM a
-addPathEntry nm = withState (\st -> st { declPath = declPath st ++ [nm] })
+addPathEntry nm = withEnv (\ env -> env { declPath = declPath env ++ [nm] })
 
 getPathEntry :: TM [String]
-getPathEntry = declPath `liftM` getState
+getPathEntry = declPath `liftM` getEnv
 
 bindLocals :: [Id] -> TM a -> TM a
 bindLocals new_ids (TM m)
-  = TM $ \ st -> case m st{ inScope = inScope st `extendVarSetList` new_ids } of
+  = TM $ \ env st -> 
+                 case m env st{ inScope = inScope st `extendVarSetList` new_ids } of
                    (r, fv, st') -> (r, fv `delListFromUFM` occs, st')
   where occs = [ nameOccName (idName id) | id <- new_ids ] 
 
 -- the tick application inherits the source position of its
 -- expression argument to support nested box allocations 
 allocTickBox :: BoxLabel -> SrcSpan -> TM (HsExpr Id) -> TM (LHsExpr Id)
-allocTickBox boxLabel pos m | isGoodSrcSpan pos = do
+allocTickBox boxLabel pos m | isGoodSrcSpan' pos = do
   (fvs, e) <- getFreeVars m
-  TM $ \st ->
+  TM $ \ env st ->
     let c = tickBoxCount st
         ids = occEnvElts fvs
         mes = mixEntries st
@@ -587,7 +612,7 @@ allocTickBox boxLabel pos m = do e <- m; return (L pos e)
 -- the tick application inherits the source position of its
 -- expression argument to support nested box allocations 
 allocATickBox :: BoxLabel -> SrcSpan -> FreeVars -> TM (Maybe (Int,[Id]))
-allocATickBox boxLabel pos fvs | isGoodSrcSpan pos = TM $ \ st ->
+allocATickBox boxLabel pos fvs | isGoodSrcSpan' pos = TM $ \ env st ->
   let me = (pos, map (nameOccName.idName) ids, boxLabel)
       c = tickBoxCount st
       mes = mixEntries st
@@ -599,10 +624,10 @@ allocATickBox boxLabel pos fvs | isGoodSrcSpan pos = TM $ \ st ->
 allocATickBox boxLabel pos fvs = return Nothing
 
 allocBinTickBox :: (Bool -> BoxLabel) -> LHsExpr Id -> TM (LHsExpr Id)
-allocBinTickBox boxLabel (L pos e) | isGoodSrcSpan pos = TM $ \ st ->
+allocBinTickBox boxLabel (L pos e) | isGoodSrcSpan' pos = TM $ \ env st ->
   let meT = (pos,[],boxLabel True)
       meF = (pos,[],boxLabel False)
-      meE = (pos,[],ExpBox)
+      meE = (pos,[],ExpBox False)
       c = tickBoxCount st
       mes = mixEntries st
   in 
@@ -622,20 +647,29 @@ allocBinTickBox boxLabel (L pos e) | isGoodSrcSpan pos = TM $ \ st ->
 
 allocBinTickBox boxLabel e = return e
 
-mkHpcPos :: SrcSpan -> Maybe HpcPos
+isGoodSrcSpan' pos
+   | not (isGoodSrcSpan pos) = False
+   | start == end            = False
+   | otherwise              = True
+  where
+   start = srcSpanStart pos
+   end   = srcSpanEnd pos
+
+mkHpcPos :: SrcSpan -> HpcPos
 mkHpcPos pos 
-   | not (isGoodSrcSpan pos) = Nothing
-   | start == end            = Nothing -- no actual location
-   | otherwise              = Just hpcPos
+   | not (isGoodSrcSpan' pos) = panic "bad source span; expected such spans to be filtered out"
+   | otherwise               = hpcPos
   where
    start = srcSpanStart pos
    end   = srcSpanEnd pos
    hpcPos = toHpcPos ( srcLocLine start
-                    , srcLocCol start
+                    , srcLocCol start + 1
                     , srcLocLine end
                     , srcLocCol end
                     )
 
+noHpcPos = toHpcPos (0,0,0,0)
+
 hpcSrcSpan = mkGeneralSrcSpan (FSLIT("Haskell Program Coverage internals"))
 \end{code}
 
@@ -649,21 +683,18 @@ matchesOneOfMany lmatches = sum (map matchCount lmatches) > 1
 
 
 \begin{code}
----------------------------------------------------------------
--- Datatypes and file-access routines for the per-module (.mix)
--- indexes used by Hpc.
--- Colin Runciman and Andy Gill, June 2006
----------------------------------------------------------------
-
--- a module index records the attributes of each tick-box that has
--- been introduced in that module, accessed by tick-number position
--- in the list
+-- | 'Mix' is the information about a modules static properties, like 
+-- location of Tix's in a file.
+-- tab stops are the size of a tab in the provided line:colunm values.
+--  * In GHC, this is 1 (a tab is just a character)
+--  * With hpc-tracer, this is 8 (a tab represents several spaces).
 
 data Mix = Mix 
-            FilePath           -- location of original file
-            Integer            -- time (in seconds) of original file's last update, since 1970.
-            Int                -- tab stop value 
-            [MixEntry_]        -- entries
+            FilePath           -- ^location of original file
+            Integer            -- ^time (in seconds) of original file's last update, since 1970.
+            Int                -- ^hash of mix entry + timestamp
+            Int                -- ^tab stop value.
+            [MixEntry_]        -- ^entries
         deriving (Show, Read)
 
 -- We would rather use ClockTime in Mix, but ClockTime has no Read instance in 6.4 and before,
@@ -673,19 +704,27 @@ data Mix = Mix
 type MixEntry  = (SrcSpan, [OccName], BoxLabel)
 type MixEntry_ = (HpcPos, BoxLabel)
 
-data BoxLabel = ExpBox
-              | AltBox
+data BoxLabel = ExpBox Bool    -- isAlt
               | TopLevelBox [String]
               | LocalBox [String]
-              | GuardBinBox Bool
-              | CondBinBox Bool
-              | QualBinBox Bool
-             | ExternalBox String HpcPos
-                  -- ^The position was generated from the named file/module,
-                  -- with the stated position (inside the named file/module).
-                  -- The HpcPos inside this MixEntry refers to the generated Haskell location.
-              deriving (Read, Show)
-                         
+             | BinBox CondBox Bool
+              deriving (Read, Show, Eq, Ord)
+
+data CondBox = GuardBinBox
+             | CondBinBox
+             | QualBinBox
+              deriving (Read, Show, Eq, Ord)
+
+-- For the hash value, we hash everything: the file name, 
+--  the timestamp of the original source file, the tab stop,
+--  and the mix entries. We cheat, and hash the show'd string.
+-- This hash only has to be hashed at Mix creation time,
+-- and is for sanity checking only.
+
+mixHash :: FilePath -> Integer -> Int -> [MixEntry_] -> Int
+mixHash file tm tabstop entries = fromIntegral $ hashString
+       (show $ Mix file tm 0 tabstop entries)
+
 mixCreate :: String -> String -> Mix -> IO ()
 mixCreate dirName modName mix =
    writeFile (mixName dirName modName) (show mix)
index 78dd841..f1b9622 100644 (file)
@@ -59,7 +59,7 @@ module HscTypes (
        Linkable(..), isObjectLinkable,
        Unlinked(..), CompiledByteCode,
        isObject, nameOfObject, isInterpretable, byteCodeOfObject,
-        HpcInfo, noHpcInfo,
+        HpcInfo(..), noHpcInfo,
 
         -- Breakpoints
         ModBreaks (..), BreakIndex, emptyModBreaks
@@ -1193,10 +1193,14 @@ showModMsg target recomp mod_summary
 %************************************************************************
 
 \begin{code}
-type HpcInfo = Int             -- just the number of ticks in a module
+data HpcInfo = HpcInfo 
+     { hpcInfoTickCount :: Int 
+     , hpcInfoHash      :: Int  
+     }
+     | NoHpcInfo
 
 noHpcInfo :: HpcInfo
-noHpcInfo = 0                  -- default = 0
+noHpcInfo = NoHpcInfo
 \end{code}
 
 %************************************************************************
index 0c606e9..62764e1 100644 (file)
@@ -71,7 +71,7 @@ extern void*  createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr,
 extern void   freeHaskellFunctionPtr(void* ptr);
 
 /* Hpc stuff */
-extern int hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr);
+extern int hs_hpc_module(char *modName,int modCount,int modHashNo,StgWord64 *tixArr);
 extern void hs_hpc_tick(int globIx,struct StgTSO_ *current_tso);
 extern void hs_hpc_raise_event(struct StgTSO_ *current_tso);   
 extern void hs_hpc_thread_finished_event(struct StgTSO_ *current_tso);
index c474d81..b183253 100644 (file)
--- a/rts/Hpc.c
+++ b/rts/Hpc.c
 #define WOP_SIZE 1024  
 
 static int hpc_inited = 0;             // Have you started this component?
-static int totalTickCount = 0;         // How many ticks have we got to work with
 static FILE *tixFile;                  // file being read/written
 static int tix_ch;                     // current char
-static StgWord64 magicTixNumber;       // Magic/Hash number to mark .tix files
 
 static FILE *rixFile = NULL;           // The tracer file/pipe (to debugger)
 static FILE *rixCmdFile = NULL;                // The tracer file/pipe (from debugger)
@@ -46,6 +44,7 @@ typedef struct _Info {
   char *modName;               // name of module
   int tickCount;               // number of ticks
   int tickOffset;              // offset into a single large .tix Array
+  int hashNo;                  // Hash number for this module's mix info
   StgWord64 *tixArr;           // tix Array from the program execution (local for this module)
   struct _Info *next;
 } Info;
@@ -59,7 +58,6 @@ typedef struct _Info {
 
 Info *modules = 0;
 Info *nextModule = 0;
-StgWord64 *tixBoxes = 0;       // local copy of tixBoxes array, from file.
 int totalTixes = 0;            // total number of tix boxes.
 
 static char *tixFilename;
@@ -67,7 +65,11 @@ static char *tixFilename;
 static void failure(char *msg) {
   debugTrace(DEBUG_hpc,"hpc failure: %s\n",msg);
   fprintf(stderr,"Hpc failure: %s\n",msg);
-  fprintf(stderr,"(perhaps remove .tix file?)\n");
+  if (tixFilename) {
+    fprintf(stderr,"(perhaps remove %s file?)\n",tixFilename);
+  } else {
+    fprintf(stderr,"(perhaps remove .tix file?)\n");
+  }
   exit(-1);
 }
 
@@ -83,6 +85,7 @@ static int init_open(char *filename)
 
 static void expect(char c) {
   if (tix_ch != c) {
+    fprintf(stderr,"('%c' '%c')\n",tix_ch,c);
     failure("parse error when reading .tix file");
   }
   tix_ch = getc(tixFile);
@@ -126,7 +129,6 @@ static void hpc_init(void) {
     return;
   }
   hpc_inited = 1;
-  
 
   tixFilename = (char *) malloc(strlen(prog_name) + 6);
   sprintf(tixFilename, "%s.tix", prog_name);
@@ -139,28 +141,42 @@ static void hpc_init(void) {
     expect('i');
     expect('x');
     ws();
-    magicTixNumber = expectWord64();
-    ws();
     expect('[');
     ws();
     while(tix_ch != ']') {
       tmpModule = (Info *)calloc(1,sizeof(Info));
-      expect('(');
+      expect('T');
+      expect('i');
+      expect('x');
+      expect('M');
+      expect('o');
+      expect('d');
+      expect('u');
+      expect('l');
+      expect('e');
       ws();
       tmpModule -> modName = expectString();
       ws();
-      expect(',');
+      tmpModule -> hashNo = (unsigned int)expectWord64();
       ws();
       tmpModule -> tickCount = (int)expectWord64();
-      ws();
-      expect(')');
-      ws();
-      
+      tmpModule -> tixArr = (StgWord64 *)calloc(tmpModule->tickCount,sizeof(StgWord64));
       tmpModule -> tickOffset = totalTixes;
       totalTixes += tmpModule -> tickCount;
-      
-      tmpModule -> tixArr = 0;
-      
+      ws();
+      expect('[');
+      ws();
+      for(i = 0;i < tmpModule->tickCount;i++) {
+       tmpModule->tixArr[i] = expectWord64();
+       ws();
+       if (tix_ch == ',') {
+         expect(',');
+         ws();
+       }
+      }
+      expect(']');
+      ws();
+
       if (!modules) {
        modules = tmpModule;
       } else {
@@ -174,24 +190,7 @@ static void hpc_init(void) {
       }
     }
     expect(']');
-    ws();
-    tixBoxes = (StgWord64 *)calloc(totalTixes,sizeof(StgWord64));
-
-    expect('[');
-    for(i = 0;i < totalTixes;i++) {
-      if (i != 0) {
-       expect(',');
-       ws();
-      }
-    tixBoxes[i] = expectWord64();
-    ws();
-    }
-    expect(']');
-
     fclose(tixFile);
-  } else {
-    // later, we will find a binary specific 
-    magicTixNumber = (StgWord64)0;
   }
 }
 
@@ -201,7 +200,10 @@ static void hpc_init(void) {
  */
 
 int
-hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
+hs_hpc_module(char *modName,
+             int modCount,
+             int modHashNo,
+             StgWord64 *tixArr) {
   Info *tmpModule, *lastModule;
   int i;
   int offset = 0;
@@ -218,12 +220,18 @@ hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
       if (tmpModule->tickCount != modCount) {
        failure("inconsistent number of tick boxes");
       }
-      assert(tmpModule->tixArr == 0);  
-      assert(tixBoxes != 0);
-      tmpModule->tixArr = tixArr;
+      assert(tmpModule->tixArr != 0);  
+      if (tmpModule->hashNo != modHashNo) {
+       fprintf(stderr,"in module '%s'\n",tmpModule->modName);
+       failure("module mismatch with .tix/.mix file hash number");
+       fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
+       exit(-1);
+
+      }
       for(i=0;i < modCount;i++) {
-       tixArr[i] = tixBoxes[i + tmpModule->tickOffset];
+       tixArr[i] = tmpModule->tixArr[i];
       }
+      tmpModule->tixArr = tixArr;
       return tmpModule->tickOffset;
     }
     lastModule = tmpModule;
@@ -232,6 +240,7 @@ hs_hpc_module(char *modName,int modCount,StgWord64 *tixArr) {
   tmpModule = (Info *)calloc(1,sizeof(Info));
   tmpModule->modName = modName;
   tmpModule->tickCount = modCount;
+  tmpModule->hashNo = modHashNo;
   if (lastModule) {
     tmpModule->tickOffset = lastModule->tickOffset + lastModule->tickCount;
   } else {
@@ -441,7 +450,6 @@ breakPointCommand(HpcRixOp rixOp, StgThreadID rixTid) {
 
 void
 startupHpc(void) {
-  Info *tmpModule;
   char *hpcRix;
 
   debugTrace(DEBUG_hpc,"startupHpc");
@@ -449,21 +457,6 @@ startupHpc(void) {
  if (hpc_inited == 0) {
     return;
   }
-
-  tmpModule = modules;
-
-  if (tixBoxes) {
-    for(;tmpModule != 0;tmpModule = tmpModule->next) {
-      totalTickCount += tmpModule->tickCount;
-      if (!tmpModule->tixArr) {
-       fprintf(stderr,"error: module %s did not register any hpc tick data\n",
-               tmpModule->modName);
-       fprintf(stderr,"(perhaps remove %s ?)\n",tixFilename);
-       exit(-1);
-      }
-    }
-  }
-
   // HPCRIX contains the name of the file to send our dynamic runtime output to (a named pipe).
 
   hpcRix = getenv("HPCRIX");
@@ -514,10 +507,11 @@ startupHpc(void) {
 
       tixCount += tmpModule->tickCount;
 
-      debugTrace(DEBUG_hpc,"(tracer)%s: %u (offset=%u)\n",
-             tmpModule->modName,
-             tmpModule->tickCount,
-             tmpModule->tickOffset);
+      debugTrace(DEBUG_hpc,"(tracer)%s: %u (offset=%u) (hash=%u)\n",
+                tmpModule->modName,
+                tmpModule->tickCount,
+                tmpModule->hashNo,
+                tmpModule->tickOffset);
 
     }
     fprintf(rixFile,"]\n");
@@ -538,7 +532,7 @@ startupHpc(void) {
 void
 exitHpc(void) {
   Info *tmpModule;  
-  int i, comma;
+  int i, inner_comma, outer_comma;
 
   debugTrace(DEBUG_hpc,"exitHpc");
 
@@ -548,27 +542,45 @@ exitHpc(void) {
 
   FILE *f = fopen(tixFilename,"w");
   
-  comma = 0;
+  outer_comma = 0;
 
-  fprintf(f,"Tix %" PRIuWORD64 " [", magicTixNumber);
+  fprintf(f,"Tix [");
   tmpModule = modules;
   for(;tmpModule != 0;tmpModule = tmpModule->next) {
-    if (comma) {
+    if (outer_comma) {
       fprintf(f,",");
     } else {
-      comma = 1;
+      outer_comma = 1;
     }
-    fprintf(f,"(\"%s\",%u)",
+    fprintf(f," TixModule \"%s\" %u %u [",
           tmpModule->modName,
+           tmpModule->hashNo,
            tmpModule->tickCount);
-    debugTrace(DEBUG_hpc,"%s: %u (offset=%u)\n",
-          tmpModule->modName,
-          tmpModule->tickCount,
-          tmpModule->tickOffset);
+    debugTrace(DEBUG_hpc,"%s: %u (offset=%u) (hash=%u)\n",
+              tmpModule->modName,
+              tmpModule->tickCount,
+              tmpModule->hashNo,
+              tmpModule->tickOffset);
+
+    inner_comma = 0;
+    for(i = 0;i < tmpModule->tickCount;i++) {
+      if (inner_comma) {
+       fprintf(f,",");
+      } else {
+       inner_comma = 1;
+      }
+
+      if (tmpModule->tixArr) {
+       fprintf(f,"%" PRIuWORD64,tmpModule->tixArr[i]);
+      } else {
+       fprintf(f,"0");
+      }
+    }
+    fprintf(f,"]");
   }
-  fprintf(f,"] [");
+  fprintf(f,"]\n");
   
-  comma = 0;
+  /*
   tmpModule = modules;
   for(;tmpModule != 0;tmpModule = tmpModule->next) {
       if (!tmpModule->tixArr) {
@@ -594,6 +606,7 @@ exitHpc(void) {
   }
       
   fprintf(f,"]\n");
+  */
   fclose(f);
 
   if (rixFile != NULL) {