packages feed

ghc-debug-client-0.1.0.0: src/GHC/Debug/Client/Monad/Haxl.hs

{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GADTs #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module GHC.Debug.Client.Monad.Haxl
  ( Debuggee(..)
  , Request(..)
  , DebugM
  , Env(..)
  ) where

import GHC.Debug.Types
import qualified Data.HashMap.Strict as HM
import System.IO
import Control.Concurrent

import GHC.Debug.Client.BlockCache
import GHC.Debug.Client.Monad.Class

import Haxl.Core hiding (Request, env)
import Data.Typeable

import Data.IORef

data Debuggee = Debuggee { -- Keep track of how many of each request we make
                           debuggeeRequestCount :: IORef (HM.HashMap CommandId Int)
                         , debuggeeBatchMode :: BatchMode
                         }

data BatchMode = Batch | OneByOne deriving (Eq, Show)

instance DebugMonad (GenHaxl Debuggee String) where
  type DebugEnv (GenHaxl Debuggee String) = Env Debuggee String
  request = dataFetch
  requestBlock = dataFetch
  traceMsg = tellWrite
  printRequestLog = traceRequestLog
  runDebug = runHaxl
  runDebugTrace = runHaxlWithWrites
  newEnv args =
    case args of
      SnapshotMode _e -> error "Loading from snapshot not supported"
      SocketMode h -> mkEnv h

  saveCache = error "TODO"
  loadCache = error "TODO"
  unsafeLiftIO = error "TODO"

type DebugM = GenHaxl Debuggee String


{- MP:
- In some profiles it seemed that the caching step was causing quite a bit
- of overhead, but still using the cache is about 2-3x faster than without
- a cache. (ie using `doRequest` directly or `uncachedRequest`.
-}

-- | Send a request to a 'Debuggee' paused with 'withPause'.
--request :: (Show resp, Typeable resp) => Request resp -> DebugM resp
--request = dataFetch


instance StateKey Request where
  data State Request = RequestState (MVar Handle)

instance DataSourceName Request where
  dataSourceName Proxy = "ghc-debug"

instance ShowP Request where
  showp = show

{-
-- | Group together RequestClosures and RequestInfoTables to avoid
-- some context switching.
groupFetches :: MVar Handle -> [([ClosurePtr], ResultVar [RawClosure])] -> [([InfoTablePtr], ResultVar [(StgInfoTableWithPtr, RawInfoTable)])] -> [BlockedFetch Request] -> [BlockedFetch Request] -> IO ()
groupFetches h cs is todo [] = dispatch h cs is (reverse todo)
groupFetches h cs is todo (b@(BlockedFetch r resp) : bs) =
  case r of
    RequestInfoTables is' -> groupFetches h cs ((is', resp):is) todo bs
    RequestClosures cs' -> groupFetches h ((cs', resp):cs) is todo bs
    _ -> groupFetches h cs is (b:todo) bs

dispatch :: MVar Handle
         -> [([ClosurePtr], ResultVar [RawClosure])]
         -> [([InfoTablePtr], ResultVar [(StgInfoTableWithPtr, RawInfoTable)])]
         -> [BlockedFetch Request]
         -> IO ()
dispatch h cs its other = do
  mapM_ do_one other
  -- These can be used to inspect how much batching is happening
--  print ("BATCHED_CLOSURES", length cs, map fst cs)
--  print (length its, map fst its)
  do_many RequestClosures cs
  do_many RequestInfoTables its
  where
    do_one (BlockedFetch req resp) = do
      res <- doRequest h req
      putSuccess resp res

    do_many :: ([a] -> Request [b]) -> [([a], ResultVar [b])] -> IO ()
    do_many _ [] = return ()
    do_many mk_req ms = do
      let req = mk_req (concatMap fst ms)
      results <- doRequest h req
      recordResults results ms



-- | Write the correct number of results to each result var
recordResults :: [a] -> [([b], ResultVar [a])] -> IO ()
recordResults [] [] = return ()
recordResults res ((length -> n, rvar):xs) =
  putSuccess rvar here >> recordResults later xs
  where
    (here, later) = splitAt n res
recordResults _ _ = error ("Impossible recordResults")
-}

_singleFetches :: MVar Handle -> [BlockedFetch Request] -> IO ()
_singleFetches h bs = mapM_ do_one bs
      where
        do_one (BlockedFetch req resp) = do
          res <- doRequest h req
          putSuccess resp res

instance DataSource Debuggee Request where
  fetch (RequestState h) _fs u =
    -- Grouping together fetches only shaves off about 0.01s on the simple
    -- benchmark
    SyncFetch $
      case debuggeeBatchMode u of
        --Batch -> groupFetches h [] [] []
        _ -> _singleFetches h



instance StateKey BlockCacheRequest where
  data State BlockCacheRequest = BCRequestState (IORef BlockCache) (MVar Handle)

instance DataSourceName BlockCacheRequest where
  dataSourceName Proxy = "block-cache"

instance ShowP BlockCacheRequest where
  showp = show


instance DataSource u BlockCacheRequest where
  fetch (BCRequestState ref h) _fs _u =
    SyncFetch (mapM_ do_one)
    where
      do_one :: BlockedFetch BlockCacheRequest -> IO ()
      do_one (BlockedFetch bcr resp) = do
        res <- handleBlockReq (doRequest h) ref bcr
        putSuccess resp res



mkEnv :: Handle -> IO (Env Debuggee String)
mkEnv hdl = do
  requestMap <- newIORef HM.empty
  bc <- newIORef emptyBlockCache
  mhdl <- newMVar hdl
  let ss = stateSet (BCRequestState bc mhdl) (stateSet (RequestState mhdl) stateEmpty)
  new_env <- initEnv ss (Debuggee requestMap Batch)
  -- Turn on data fetch stats with report = 3
  let new_flags = defaultFlags { report = 0 }
  return $ new_env { Haxl.Core.flags = new_flags }

-- | Print out the number of request made for each request type
traceRequestLog :: Env u w -> IO ()
traceRequestLog d = do
  s <- readIORef (statsRef d)
  putStrLn (ppStats s)

_traceProfile :: Env u w -> IO ()
_traceProfile e = do
  p <- readIORef (profRef e)
  print (profile p)