packages feed

hpc-tracer-0.3.0: src/TixStreamActor.hs

-- TODO: renamed as RixStreamActor

module TixStreamActor (readTickActor, StreamActorResult(..), finished) where

import Data.List as List
import System.IO
import Reactive

import Common

import Debug.Trace
import Data.Set(Set)
import qualified Data.Set as Set

data State = State 
     { cursor 	  :: ThreadedEvent
     , count      :: !Integer			-- where we are	
     , target     :: !Integer			-- where we are going to
     , before     :: ![ThreadedEvent]
     , after      :: ![ThreadedEvent]
     , stopAtTicks :: Set GlobalTickId
     , remoteStopAtTicks 
	          :: Set GlobalTickId
     } deriving (Show)


finished :: State -> Bool
finished state = case cursor state of
	           ThreadedEvent Finished _ -> True
    	           _ -> False 

-- Move this into TixStream.
data ThreadedEvent = ThreadedEvent !Event !ThreadID
      deriving (Show)

addEvent :: Event -> State -> ThreadedEvent
addEvent ev (State { cursor = ThreadedEvent _ tid })  = ThreadedEvent ev tid

data StreamActorResult 
   = StreamActorResult 
     { counter  :: !Integer		-- ^global tick counter
     , event    :: !Event 		-- ^event at the cursor
     , threadId :: !ThreadID		-- ^the current thread (before/at the event)
     , live     :: !Bool		-- ^is this the live event (False == historical result)
     }

-- move forward or back, finding the tick number.
readTickActor :: Handle -> Handle -> IO (Integer -> Set GlobalTickId -> IO StreamActorResult)
readTickActor d_handle cmd_handle = do
  -- Will this help
--  t <- hGetBuffering d_handle
--  print t

--  hSetBuffering d_handle (LineBuffering)
--  hSetBuffering cmd_handle (LineBuffering)

  tixTxt <- hGetLine d_handle
  let (0,threadId',event') = parseEvent tixTxt

  let initState = 
        State { cursor = ThreadedEvent event' threadId'
      	      , count = 0
	      , target = 0
  	      , before = []
	      , after = []
	      , stopAtTicks = Set.empty
	      , remoteStopAtTicks = Set.empty
	      }

  let pull' :: Integer -> Set GlobalTickId -> State -> IO State
      pull' n ticks state = pull $ state { target = n
      	      	  	       	         , stopAtTicks = ticks
					 }

      -- check for breakpoint
      check :: State -> IO State
      check state@State { stopAtTicks = stopTicks, cursor = ThreadedEvent (Tick tickid) _ }
         | tickid `Set.member` stopTicks = return state
      check state = pull state

      pull :: State -> IO State
      pull state@State { target = n , count = c } 
--        | trace (show state) False = undefined

	  -- reached target
      	| n == c = return state

	| n > c && null (after state) && not (finished state) = do
	         -- We need to read a new entry from the remote process
		 -- set break at the target
		 hPutStrLn cmd_handle ("c" ++ show n) -- set counter bp
		 sequence [ do hPutStrLn cmd_handle ("u" ++ show x) 
--		 	       print ("unseting bp at ",x)
		 	  | x <- Set.elems $ remoteStopAtTicks state
			  ]
		 sequence [ do hPutStrLn cmd_handle ("s" ++ show x) 
--		 	       print ("seting bp at ",x)
		 	  | x <- Set.elems $ stopAtTicks state
			  ]
		 hPutStrLn cmd_handle ("")            -- run
		 hFlush cmd_handle
		 	   
--                 print ("waiting for message")
		 -- and wait for the breakpoint to activate
		 tixTxt <- hGetLine d_handle
--                 print ("got : ",tixTxt)
--		 print tixTxt
		 let (count',threadId',event') = parseEvent tixTxt

		 let before' = if c == count' - 1
		     	       then cursor state : before state
  			       else []

                 -- something interesting happened so we return anyway.

		 return $ state { cursor = ThreadedEvent event' threadId' 
		     	       , count = count'
			       , before = before'
			       , remoteStopAtTicks = stopAtTicks state
			       }

	| n > c && not (null $ after state) = 
                 check (state { cursor = head $! after state 
		      	      , count  = count state + 1
			      , before = cursor state : before state
			      , after  = tail $! after state
			      })

	| n < c && (null $ before state) && (current_loc /= 0) = do
	         -- we want to rewind here, so we pull some history
		 hPutStrLn cmd_handle ("h")           -- history
		 hFlush cmd_handle

		 befores <-
		   sequence [ do tixTxt <- hGetLine d_handle
--		                 print ("got(h): ",tixTxt)
			         let (count',threadId',event') = parseEvent tixTxt
			         if count' /= ix 
			           then error $ "strangeness" ++ show (ix,count')
			           else return $ ThreadedEvent event' threadId'
		 	    | ix <- [(max 0 (current_loc - 1024)) .. (current_loc - 1) ]
			    ]

                 pull $ state { before = drop (length $ after state) $ reverse befores }

	| n < c && not (null $ before state) = 
	         check (state { cursor = head $! before state 
		      	      , count  = count state - 1
			      , before = tail $! before state
			      , after  = cursor state : after state
			      })

	| otherwise = return state

       where 
    	  current_loc = c + fromIntegral (length (after state))


  mkActor2 initState $ \ request action targetCount tickBreakPoints ->
       request $ \ state -> do
	  state' <- pull' targetCount tickBreakPoints state
	  let (ThreadedEvent event tid) = cursor state'
	  let isLive = not (List.null $ after state')
	  return (StreamActorResult (count state') event tid isLive,state')


parseEvent :: String -> (Integer,ThreadID,Event)
parseEvent xs = case words xs of
	         ["Event",counter,tid,op] 
		   -> (read counter, read tid, read op)
		 _ -> error $ "parseEvent failure: " ++ show xs