module GHC.Stack.Profiler.Internal.Manager (
Manager (..),
newManager,
stopManager,
shouldProfile,
enableEventLogging,
disableEventLogging,
enableSampling,
disableSampling,
registerSamplerThread,
unregisterSamplerThread,
stopAllSamplerThreads,
Sampler (..),
cancelSampler,
EventLoop (..),
startEventLoop,
stopEventLoop,
ControlMessage (..),
startProfiling,
stopProfiling,
sendPublishInitEventMessages,
sendStartProfilingMessage,
sendStopProfilingMessage,
sendEnableEventlogMessage,
sendDisableEventlogMessage,
) where
import Control.Concurrent (ThreadId)
import Control.Concurrent.Async (Async (..), async, cancel, link)
import Control.Concurrent.Chan
import Control.Concurrent.MVar
import Control.Concurrent.STM (STM)
import Control.Concurrent.STM.TVar
import qualified Control.Concurrent.STM.TVar as STM
import qualified Control.Concurrent.STM.TVar as TVar
import Control.Monad (forever)
import Control.Monad.STM (atomically)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as BSL
import Data.Foldable (for_)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import qualified Debug.Trace
import qualified Debug.Trace.Binary.Compat as Compat
import GHC.Generics (Generic)
import qualified GHC.Stack.Profiler.Core as GSPC (Message (ProtocolVersion), ProtocolVersion (MyProtocolVersion))
import qualified GHC.Stack.Profiler.Internal.Decode as Decode
import GHC.Stack.Profiler.Internal.SymbolTable
data Manager = MkManager
{ Manager -> TVar (Map ThreadId Sampler)
samplerThreadMapVar :: !(TVar (Map ThreadId Sampler))
, Manager -> TVar (Maybe EventLoop)
eventLoopThreadVar :: !(TVar (Maybe EventLoop))
, Manager -> StackSymbolTable
symbolTableRef :: !StackSymbolTable
, Manager -> TVar Bool
shouldSampleVar :: !(TVar Bool)
, Manager -> TVar Bool
eventLoggingStartedVar :: !(TVar Bool)
, Manager -> Chan ControlMessage
messageChan :: Chan ControlMessage
}
deriving ((forall x. Manager -> Rep Manager x)
-> (forall x. Rep Manager x -> Manager) -> Generic Manager
forall x. Rep Manager x -> Manager
forall x. Manager -> Rep Manager x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Manager -> Rep Manager x
from :: forall x. Manager -> Rep Manager x
$cto :: forall x. Rep Manager x -> Manager
to :: forall x. Rep Manager x -> Manager
Generic, Manager -> Manager -> Bool
(Manager -> Manager -> Bool)
-> (Manager -> Manager -> Bool) -> Eq Manager
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Manager -> Manager -> Bool
== :: Manager -> Manager -> Bool
$c/= :: Manager -> Manager -> Bool
/= :: Manager -> Manager -> Bool
Eq)
newManager :: Bool -> IO Manager
newManager :: Bool -> IO Manager
newManager Bool
wait = do
tracingEnabled <- IO Bool
Compat.userTracingEnabledIO
samplerThreadMapVar <- newTVarIO Map.empty
eventLoopThreadVar <- newTVarIO Nothing
symbolTableRef <- emptySymbolTableIO
shouldSampleVar <- newTVarIO (not wait)
eventLoggingStartedVar <- newTVarIO tracingEnabled
messageChan <- newChan
pure
MkManager
{ samplerThreadMapVar
, eventLoopThreadVar
, symbolTableRef
, shouldSampleVar
, eventLoggingStartedVar
, messageChan
}
stopManager :: Manager -> IO ()
stopManager :: Manager -> IO ()
stopManager Manager
manager = do
Manager -> IO ()
stopAllSamplerThreads Manager
manager
Manager -> IO ()
stopEventLoop Manager
manager
shouldProfile :: Manager -> STM Bool
shouldProfile :: Manager -> STM Bool
shouldProfile Manager
manager =
(Bool -> Bool -> Bool) -> STM Bool -> STM Bool -> STM Bool
forall a b c. (a -> b -> c) -> STM a -> STM b -> STM c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2
Bool -> Bool -> Bool
(&&)
(TVar Bool -> STM Bool
forall a. TVar a -> STM a
readTVar (TVar Bool -> STM Bool) -> TVar Bool -> STM Bool
forall a b. (a -> b) -> a -> b
$ Manager -> TVar Bool
shouldSampleVar Manager
manager)
(TVar Bool -> STM Bool
forall a. TVar a -> STM a
readTVar (TVar Bool -> STM Bool) -> TVar Bool -> STM Bool
forall a b. (a -> b) -> a -> b
$ Manager -> TVar Bool
eventLoggingStartedVar Manager
manager)
enableEventLogging :: Manager -> STM ()
enableEventLogging :: Manager -> STM ()
enableEventLogging Manager
manager = do
TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
TVar.writeTVar (Manager -> TVar Bool
eventLoggingStartedVar Manager
manager) Bool
True
disableEventLogging :: Manager -> STM ()
disableEventLogging :: Manager -> STM ()
disableEventLogging Manager
manager = do
TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
TVar.writeTVar (Manager -> TVar Bool
eventLoggingStartedVar Manager
manager) Bool
False
enableSampling :: Manager -> STM ()
enableSampling :: Manager -> STM ()
enableSampling Manager
manager = do
TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
TVar.writeTVar (Manager -> TVar Bool
shouldSampleVar Manager
manager) Bool
True
disableSampling :: Manager -> STM ()
disableSampling :: Manager -> STM ()
disableSampling Manager
manager = do
TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
TVar.writeTVar (Manager -> TVar Bool
shouldSampleVar Manager
manager) Bool
False
registerSamplerThread :: Manager -> Sampler -> IO ()
registerSamplerThread :: Manager -> Sampler -> IO ()
registerSamplerThread Manager
manager samplerThread :: Sampler
samplerThread@MkSampler{Async ()
samplerAsync :: Async ()
samplerAsync :: Sampler -> Async ()
samplerAsync} = do
Async () -> IO ()
forall a. Async a -> IO ()
link Async ()
samplerAsync
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
TVar (Map ThreadId Sampler)
-> (Map ThreadId Sampler -> Map ThreadId Sampler) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
STM.modifyTVar' (Manager -> TVar (Map ThreadId Sampler)
samplerThreadMapVar Manager
manager) ((Map ThreadId Sampler -> Map ThreadId Sampler) -> STM ())
-> (Map ThreadId Sampler -> Map ThreadId Sampler) -> STM ()
forall a b. (a -> b) -> a -> b
$ \Map ThreadId Sampler
threadMap ->
(ThreadId -> Sampler -> Map ThreadId Sampler -> Map ThreadId Sampler
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert (Async () -> ThreadId
forall a. Async a -> ThreadId
asyncThreadId Async ()
samplerAsync) Sampler
samplerThread Map ThreadId Sampler
threadMap)
unregisterSamplerThread :: Manager -> Sampler -> IO ()
unregisterSamplerThread :: Manager -> Sampler -> IO ()
unregisterSamplerThread Manager
manager MkSampler{Async ()
samplerAsync :: Sampler -> Async ()
samplerAsync :: Async ()
samplerAsync} =
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
TVar (Map ThreadId Sampler)
-> (Map ThreadId Sampler -> Map ThreadId Sampler) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
STM.modifyTVar'
(Manager -> TVar (Map ThreadId Sampler)
samplerThreadMapVar Manager
manager)
(ThreadId -> Map ThreadId Sampler -> Map ThreadId Sampler
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete (Async () -> ThreadId
forall a. Async a -> ThreadId
asyncThreadId Async ()
samplerAsync))
stopAllSamplerThreads :: Manager -> IO ()
stopAllSamplerThreads :: Manager -> IO ()
stopAllSamplerThreads Manager
manager = do
samplerThreads <-
STM [Sampler] -> IO [Sampler]
forall a. STM a -> IO a
atomically (STM [Sampler] -> IO [Sampler]) -> STM [Sampler] -> IO [Sampler]
forall a b. (a -> b) -> a -> b
$ do
samplerThreadMap <- TVar (Map ThreadId Sampler) -> STM (Map ThreadId Sampler)
forall a. TVar a -> STM a
readTVar (Manager -> TVar (Map ThreadId Sampler)
samplerThreadMapVar Manager
manager)
writeTVar (samplerThreadMapVar manager) Map.empty
pure $ Map.elems samplerThreadMap
for_ samplerThreads cancelSampler
newtype Sampler = MkSampler
{ Sampler -> Async ()
samplerAsync :: Async ()
}
cancelSampler :: Sampler -> IO ()
cancelSampler :: Sampler -> IO ()
cancelSampler MkSampler{Async ()
samplerAsync :: Sampler -> Async ()
samplerAsync :: Async ()
samplerAsync} =
Async () -> IO ()
forall a. Async a -> IO ()
cancel Async ()
samplerAsync
newtype EventLoop = MkEventLoop
{ EventLoop -> Async ()
eventLoopAsync :: Async ()
}
data ControlMessage
= WriteProfileSample [ByteString]
| PublishInitEvents (MVar ())
| StartProfiling (MVar ())
| StopProfiling (MVar ())
| StartEventlog (MVar ())
| StopEventlog (MVar ())
startEventLoop :: Manager -> IO ()
startEventLoop :: Manager -> IO ()
startEventLoop Manager
manager = do
!eventLoopThread <- do
eventLoopAsync <- IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async (IO () -> IO (Async ())) -> IO () -> IO (Async ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO ()
forall (f :: * -> *) a b. Applicative f => f a -> f b
forever (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Manager -> IO ()
eventHandler Manager
manager
link eventLoopAsync
pure $ MkEventLoop{eventLoopAsync}
atomically $ do
writeTVar (eventLoopThreadVar manager) (Just eventLoopThread)
eventHandler :: Manager -> IO ()
eventHandler :: Manager -> IO ()
eventHandler Manager
manager = do
msg <- Chan ControlMessage -> IO ControlMessage
forall a. Chan a -> IO a
readChan (Manager -> Chan ControlMessage
messageChan Manager
manager)
run <- atomically $ shouldProfile manager
case msg of
WriteProfileSample [ByteString]
msgs ->
case Bool
run of
Bool
True ->
(ByteString -> IO ()) -> [ByteString] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ByteString -> IO ()
Compat.traceBinaryEventIO [ByteString]
msgs
Bool
False ->
() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
StartProfiling MVar ()
barrier -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ Manager -> STM ()
enableSampling Manager
manager
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
barrier ()
StopProfiling MVar ()
barrier -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ Manager -> STM ()
disableSampling Manager
manager
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
barrier ()
StartEventlog MVar ()
barrier -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ Manager -> STM ()
enableEventLogging Manager
manager
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
barrier ()
StopEventlog MVar ()
barrier -> do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ Manager -> STM ()
disableEventLogging Manager
manager
MVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
barrier ()
PublishInitEvents MVar ()
barrier -> do
symbolTable <- STM (SymbolTableWriter MapTable) -> IO (SymbolTableWriter MapTable)
forall a. STM a -> IO a
atomically (STM (SymbolTableWriter MapTable)
-> IO (SymbolTableWriter MapTable))
-> STM (SymbolTableWriter MapTable)
-> IO (SymbolTableWriter MapTable)
forall a b. (a -> b) -> a -> b
$ StackSymbolTable -> STM (SymbolTableWriter MapTable)
readSymbolTable (Manager -> StackSymbolTable
symbolTableRef Manager
manager)
let
versionMessage = ProtocolVersion -> Message
GSPC.ProtocolVersion ProtocolVersion
GSPC.MyProtocolVersion
messages = Message
versionMessage Message -> [Message] -> [Message]
forall a. a -> [a] -> [a]
: SymbolTableWriter MapTable -> [Message]
Decode.initMessages SymbolTableWriter MapTable
symbolTable
messagesBytes = [Message] -> [ByteString]
Decode.serializeMessages [Message]
messages
for_ messagesBytes $ \ByteString
binaryMessage ->
ByteString -> IO ()
Compat.traceBinaryEventIO (ByteString -> ByteString
BSL.toStrict ByteString
binaryMessage)
Debug.Trace.flushEventLog
putMVar barrier ()
stopEventLoop :: Manager -> IO ()
stopEventLoop :: Manager -> IO ()
stopEventLoop Manager
manager = do
maybeEventThread <- STM (Maybe EventLoop) -> IO (Maybe EventLoop)
forall a. STM a -> IO a
atomically (STM (Maybe EventLoop) -> IO (Maybe EventLoop))
-> STM (Maybe EventLoop) -> IO (Maybe EventLoop)
forall a b. (a -> b) -> a -> b
$ TVar (Maybe EventLoop)
-> (Maybe EventLoop -> (Maybe EventLoop, Maybe EventLoop))
-> STM (Maybe EventLoop)
forall s a. TVar s -> (s -> (a, s)) -> STM a
stateTVar (Manager -> TVar (Maybe EventLoop)
eventLoopThreadVar Manager
manager) (,Maybe EventLoop
forall a. Maybe a
Nothing)
case maybeEventThread of
Maybe EventLoop
Nothing ->
() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Just MkEventLoop{Async ()
eventLoopAsync :: EventLoop -> Async ()
eventLoopAsync :: Async ()
eventLoopAsync} -> do
Manager -> IO ()
sendStopProfilingMessage Manager
manager
Async () -> IO ()
forall a. Async a -> IO ()
cancel Async ()
eventLoopAsync
startProfiling :: Manager -> IO ()
startProfiling :: Manager -> IO ()
startProfiling Manager
manager = do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (Manager -> TVar Bool
shouldSampleVar Manager
manager) Bool
True
Manager -> IO ()
sendStartProfilingMessage Manager
manager
stopProfiling :: Manager -> IO ()
stopProfiling :: Manager -> IO ()
stopProfiling Manager
manager = do
STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (Manager -> TVar Bool
shouldSampleVar Manager
manager) Bool
False
Manager -> IO ()
sendStopProfilingMessage Manager
manager
sendStartProfilingMessage :: Manager -> IO ()
sendStartProfilingMessage :: Manager -> IO ()
sendStartProfilingMessage Manager
manager = do
barrier <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
writeChan
(messageChan manager)
(StartProfiling barrier)
takeMVar barrier
sendStopProfilingMessage :: Manager -> IO ()
sendStopProfilingMessage :: Manager -> IO ()
sendStopProfilingMessage Manager
manager = do
barrier <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
writeChan
(messageChan manager)
(StopProfiling barrier)
takeMVar barrier
sendEnableEventlogMessage :: Manager -> IO ()
sendEnableEventlogMessage :: Manager -> IO ()
sendEnableEventlogMessage Manager
manager = do
barrier <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
writeChan
(messageChan manager)
(StartEventlog barrier)
takeMVar barrier
sendDisableEventlogMessage :: Manager -> IO ()
sendDisableEventlogMessage :: Manager -> IO ()
sendDisableEventlogMessage Manager
manager = do
barrier <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
writeChan
(messageChan manager)
(StopEventlog barrier)
takeMVar barrier
sendPublishInitEventMessages :: Manager -> IO ()
sendPublishInitEventMessages :: Manager -> IO ()
sendPublishInitEventMessages Manager
manager = do
barrier <- IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar
writeChan
(messageChan manager)
(PublishInitEvents barrier)
takeMVar barrier