@@ -27,7 +27,9 @@ import Control.Distributed.Process.Internal.Primitives
27 , matchAny
27 , matchAny
28 , matchAnyIf
28 , matchAnyIf
29 , handleMessage
29 , handleMessage
30− , reregister, say
32+ , reregister, say, unwrapMessage
32import Control.Distributed.Process.Internal.Trace.Types
34import Control.Distributed.Process.Internal.Trace.Types
33 ( TraceEvent(..)
35 ( TraceEvent(..)
@@ -87,9 +89,9 @@ import System.Mem.Weak
89data TracerState = TracerST {
91data TracerState = TracerST {
90− sendTrace :: !(Message -> Process ())
92+ client :: !(Maybe ProcessId)
91− , flags :: !TraceFlags
93+ , flags :: !TraceFlags
92− , regNames :: !(Map ProcessId (Set String))
94+ , regNames :: !(Map ProcessId (Set String))
95--------------------------------------------------------------------------------
97--------------------------------------------------------------------------------
@@ -104,11 +106,25 @@ defaultTracer =
104defaultTracerAux :: Process ()
106defaultTracerAux :: Process ()
105defaultTracerAux =
107defaultTracerAux =
106 catch (checkEnv "DISTRIBUTED_PROCESS_TRACE_CONSOLE" >> systemLoggerTracer)
108 catch (checkEnv "DISTRIBUTED_PROCESS_TRACE_CONSOLE" >> systemLoggerTracer)
107− (\(_ :: IOError) -> eventLogTracer)
109+ (\(_ :: IOError) -> defaultEventLogTracer)
111+-- TODO: it would be /nice/ if we had some way of checking the runtime
112+-- options to see if +RTS -v (or similar) has been given...
113+defaultEventLogTracer :: Process ()
114+defaultEventLogTracer =
115+ catch (checkEnv "DISTRIBUTED_PROCESS_TRACE_EVENTLOG" >> eventLogTracer)
116+ (\(_ :: IOError) -> nullTracer)
109checkEnv :: String -> Process String
118checkEnv :: String -> Process String
110checkEnv s = liftIO $ getEnv s
119checkEnv s = liftIO $ getEnv s
121+-- This trace client is (intentionally) useful - it simply provides
122+-- an intial client for the trace controller to talk to, until some
123+-- other (hopefully more useful) client is installed over the top of
124+-- it. This is the default trace client.
125+nullTracer :: Process ()
126+nullTracer = forever' $ receiveWait [ matchUnknown (return ()) ]
112systemLoggerTracer :: Process ()
128systemLoggerTracer :: Process ()
113systemLoggerTracer = do
129systemLoggerTracer = do
114 node <- processNode <$> ask
130 node <- processNode <$> ask
@@ -171,7 +187,10 @@ traceController mv = do
171 -- the node's internal control channel if we can possibly help it!
187 -- the node's internal control channel if we can possibly help it!
172 weakQueue <- processWeakQ <$> ask
188 weakQueue <- processWeakQ <$> ask
173 liftIO $ putMVar mv weakQueue
189 liftIO $ putMVar mv weakQueue
190+ initialTracer <- whereis "tracer.initial"
174− traceLoop initialState
191+ traceLoop initialState {
192+ client = initialTracer
176 traceLoop :: TracerState -> Process ()
195 traceLoop :: TracerState -> Process ()
177 traceLoop st = do
196 traceLoop st = do
@@ -179,22 +198,27 @@ traceController mv = do
179 -- At some point in the future, we're going to start writing these custom
198 -- At some point in the future, we're going to start writing these custom
180 -- events to the ghc eventlog, at which point this design might change.
199 -- events to the ghc eventlog, at which point this design might change.
181 st' <- receiveWait [
200 st' <- receiveWait [
182− -- we notify the previous tracer process that it has been replaced
183 match (\(setResp, set :: SetTrace) -> do
201 match (\(setResp, set :: SetTrace) -> do
184− -- We allow at most one trace target, which is a process id.
202+ -- We allow at most one trace client, which is a process id.
203+ -- Tracking multiple clients represents too high an overhead,
204+ -- so we leave that kind of thing to our consumers to figure
185 case set of
206 case set of
186 (TraceEnable pid) -> do
207 (TraceEnable pid) -> do
208+ -- notify the previous tracer it has been replaced
187 sendTrace st (createUnencodedMessage (TraceEvTakeover pid))
209 sendTrace st (createUnencodedMessage (TraceEvTakeover pid))
188− reregister "tracer" pid
210+ say $ "registering " ++ (show pid) ++ " as new tracer"
189 sendOk setResp
211 sendOk setResp
190− return st { sendTrace = (mkSender pid) }
212+ return st { client = (Just pid) }
191 TraceDisable -> do
213 TraceDisable -> do
192 sendTrace st (createUnencodedMessage TraceEvDisable)
214 sendTrace st (createUnencodedMessage TraceEvDisable)
193 sendOk setResp
215 sendOk setResp
194− return st { sendTrace = (\_ -> return ()) })
216+ say "setting sendTrace to noop"
217+ return st { client = Nothing })
195 , match (\(confResp, flags') ->
218 , match (\(confResp, flags') ->
196 sendOk confResp >> applyTraceFlags flags' st)
219 sendOk confResp >> applyTraceFlags flags' st)
197 , match (\chGetFlags -> sendChan chGetFlags (flags st) >> return st)
220 , match (\chGetFlags -> sendChan chGetFlags (flags st) >> return st)
221+ , match (\chGetCurrent -> sendChan chGetCurrent (client st) >> return st)
198 -- we dequeue incoming events even if we don't process them
222 -- we dequeue incoming events even if we don't process them
199 , matchAny (\ev ->
223 , matchAny (\ev ->
200 handleMessage ev (handleTrace st ev) >>= return . fromMaybe st)
224 handleMessage ev (handleTrace st ev) >>= return . fromMaybe st)
@@ -205,26 +229,21 @@ traceController mv = do
205 sendOk Nothing = return ()
229 sendOk Nothing = return ()
206 sendOk (Just sp) = sendChan sp TraceOk
230 sendOk (Just sp) = sendChan sp TraceOk
208− mkSender :: ProcessId -> (Message -> Process ())
209− mkSender pid = \m -> do
210− say ("sending " ++ (show m) ++ " to " ++ (show pid))
211− (flip forward) pid m
213 initialState :: TracerState
232 initialState :: TracerState
214 initialState =
233 initialState =
216− { sendTrace = (\_ -> return ())
235+ { client = Nothing
217− , flags = defaultTraceFlags
236+ , flags = defaultTraceFlags
218− , regNames = Map.empty
237+ , regNames = Map.empty
221−-- node [runtime tracer control]
240+-- note [runtime tracer control]
223-- The trace mechanism is designed to put as little stress on the system, and
242-- The trace mechanism is designed to put as little stress on the system, and
224-- in particular the node controller, as possible. The LocalNode's @tracer@
243-- in particular the node controller, as possible. The LocalNode's @tracer@
225-- field is therefore immutable, since we don't want to be blocking the node
244-- field is therefore immutable, since we don't want to be blocking the node
226-- controller when writing trace information out. The runtime cost of enabling
245-- controller when writing trace information out. The runtime cost of enabling
227−-- tracing is therefore, the cost of enqueue for all trace-able operations at
246+-- tracing is therefore the cost of enqueue for all trace-able operations at
228-- all times. If tracing is not explicitly enabled, the trace record stored in
247-- all times. If tracing is not explicitly enabled, the trace record stored in
229-- the local node state is matched and ignored in API calls, reducing the cost
248-- the local node state is matched and ignored in API calls, reducing the cost
230-- to a single function call.
249-- to a single function call.
@@ -262,13 +281,13 @@ handleTrace st msg ev = do
262 case ev of
281 case ev of
263 (TraceEvNodeDied _ _) ->
282 (TraceEvNodeDied _ _) ->
264 case (traceNodes (flags st)) of
283 case (traceNodes (flags st)) of
265− True -> (sendTrace st) msg
284+ True -> sendTrace st msg
266 False -> return ()
285 False -> return ()
267 (TraceEvUser _) -> do
286 (TraceEvUser _) -> do
268− (sendTrace st) msg
287+ sendTrace st msg
270 case (traceConnections (flags st)) of
289 case (traceConnections (flags st)) of
271− True -> (sendTrace st) msg
290+ True -> sendTrace st msg
272 False -> return ()
291 False -> return ()
273 return st
292 return st
@@ -278,14 +297,14 @@ traceEv :: TraceEvent
278 -> TracerState
297 -> TracerState
279 -> Process ()
298 -> Process ()
280traceEv _ _ Nothing _ = return ()
299traceEv _ _ Nothing _ = return ()
281−traceEv _ msg (Just TraceAll) st = (sendTrace st) msg
300+traceEv _ msg (Just TraceAll) st = sendTrace st msg
282traceEv ev msg (Just (TraceProcs pids)) st = do
301traceEv ev msg (Just (TraceProcs pids)) st = do
283 node <- processNode <$> ask
302 node <- processNode <$> ask
284 let p = case resolveToPid ev of
303 let p = case resolveToPid ev of
285 Nothing -> (nullProcessId (localNodeId node))
304 Nothing -> (nullProcessId (localNodeId node))
286 Just pid -> pid
305 Just pid -> pid
287 case (Set.member p pids) of
306 case (Set.member p pids) of
288− True -> (sendTrace st) msg
307+ True -> sendTrace st msg
289 False -> return ()
308 False -> return ()
290traceEv ev msg (Just (TraceNames names)) st = do
309traceEv ev msg (Just (TraceNames names)) st = do
291 -- TODO: if we have recorded regnames for p, then we
310 -- TODO: if we have recorded regnames for p, then we
@@ -298,5 +317,15 @@ traceEv ev msg (Just (TraceNames names)) st = do
298 Nothing -> return ()
317 Nothing -> return ()
299 Just ns -> if (Set.null (Set.intersection ns names))
318 Just ns -> if (Set.null (Set.intersection ns names))
300 then return ()
319 then return ()
320+ else sendTrace st msg
322+sendTrace :: TracerState -> Message -> Process ()
301− else (sendTrace st) msg
323+sendTrace st msg =
324+ let pid = (client st) in do
325+ (Just ev) <- unwrapMessage msg :: Process (Maybe TraceEvent)
326+ say ("sending " ++ (show ev) ++ " to " ++ (show pid))
328+ Just p -> (flip forward) p msg
329+ Nothing -> return ()