git.iko.soy/distributed-process
on master help sign in
Additional support for remote tracing
commit39fb200fc76cc9decb205c6835fa32db19a58585 authorTim Watson <tim@rabbitmq.com> dateWed 17 Apr 2013, 17:02 (13 years ago) parentd251210
13 files changed+678−325
distributed-process.cabal+2 src/Control/Distributed/Process.hs+10−110 src/Control/Distributed/Process/Closure.hs+2 src/Control/Distributed/Process/Debug.hs+45−131 src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs+24 src/Control/Distributed/Process/Internal/Primitives.hs+20−7 src/Control/Distributed/Process/Internal/Spawn.hs+168 src/Control/Distributed/Process/Internal/Trace/Primitives.hs+144−68 src/Control/Distributed/Process/Internal/Trace/Remote.hs+80 src/Control/Distributed/Process/Internal/Trace/Tracer.hs+6−5 src/Control/Distributed/Process/Internal/Trace/Types.hs+100 src/Control/Distributed/Process/Node.hs+14−4 tests/TestTracing.hs+63
distributed-process.cabal+2
@@ -65,6 +65,7 @@ Library
6565 Control.Distributed.Process.Debug,
6666 Control.Distributed.Process.Node
6767 Other-modules: Control.Distributed.Process.Internal.Primitives,
68+ Control.Distributed.Process.Internal.Spawn,
6869 Control.Distributed.Process.Internal.CQueue,
6970 Control.Distributed.Process.Internal.Closure.BuiltIn,
7071 Control.Distributed.Process.Internal.Messaging,
@@ -75,6 +76,7 @@ Library
7576 Control.Distributed.Process.Internal.Trace.Types,
7677 Control.Distributed.Process.Internal.Trace.Primitives,
7778 Control.Distributed.Process.Internal.Trace.Tracer
79+ Control.Distributed.Process.Internal.Trace.Remote
7880 Extensions: RankNTypes,
7981 ScopedTypeVariables,
8082 FlexibleInstances,
src/Control/Distributed/Process.hs+10−110
@@ -280,6 +280,15 @@ import Control.Distributed.Process.Internal.Primitives
280280 , reconnectPort
281281 )
282282import Control.Distributed.Process.Node (forkProcess)
283+import Control.Distributed.Process.Internal.Spawn
284+ ( -- Spawning Processes/Channels
285+ spawn
286+ , spawnLink
287+ , spawnMonitor
288+ , spawnChannel
289+ , spawnSupervised
290+ , call
291+ )
283292
284293-- INTERNAL NOTES
285294--
@@ -344,116 +353,6 @@ import Control.Distributed.Process.Node (forkProcess)
344353-- [6] "Delivery of Messages", post on erlang-questions
345354-- http://erlang.org/pipermail/erlang-questions/2012-February/064767.html
346355
347−--------------------------------------------------------------------------------
348−-- Primitives are defined in a separate module; here we only define derived --
349−-- constructs --
350−--------------------------------------------------------------------------------
351−
352−-- | Spawn a process
353−--
354−-- For more information about 'Closure', see
355−-- "Control.Distributed.Process.Closure".
356−--
357−-- See also 'call'.
358−spawn :: NodeId -> Closure (Process ()) -> Process ProcessId
359−spawn nid proc = do
360− us <- getSelfPid
361− mRef <- monitorNode nid
362− sRef <- spawnAsync nid (cpDelay us proc)
363− receiveWait [
364− matchIf (\(DidSpawn ref _) -> ref == sRef) $ \(DidSpawn _ pid) -> do
365− unmonitor mRef
366− send pid ()
367− return pid
368− , matchIf (\(NodeMonitorNotification ref _ _) -> ref == mRef) $ \_ ->
369− return (nullProcessId nid)
370− ]
371−
372−-- | Spawn a process and link to it
373−--
374−-- Note that this is just the sequential composition of 'spawn' and 'link'.
375−-- (The "Unified" semantics that underlies Cloud Haskell does not even support
376−-- a synchronous link operation)
377−spawnLink :: NodeId -> Closure (Process ()) -> Process ProcessId
378−spawnLink nid proc = do
379− pid <- spawn nid proc
380− link pid
381− return pid
382−
383−-- | Like 'spawnLink', but monitor the spawned process
384−spawnMonitor :: NodeId -> Closure (Process ()) -> Process (ProcessId, MonitorRef)
385−spawnMonitor nid proc = do
386− pid <- spawn nid proc
387− ref <- monitor pid
388− return (pid, ref)
389−
390−-- | Run a process remotely and wait for it to reply
391−--
392−-- We monitor the remote process: if it dies before it can send a reply, we die
393−-- too.
394−--
395−-- For more information about 'Static', 'SerializableDict', and 'Closure', see
396−-- "Control.Distributed.Process.Closure".
397−--
398−-- See also 'spawn'.
399−call :: Serializable a
400− => Static (SerializableDict a)
401− -> NodeId
402− -> Closure (Process a)
403− -> Process a
404−call dict nid proc = do
405− us <- getSelfPid
406− (pid, mRef) <- spawnMonitor nid (proc `bindCP` cpSend dict us)
407− -- We are guaranteed to receive the reply before the monitor notification
408− -- (if a reply is sent at all)
409− -- NOTE: This might not be true if we switch to unreliable delivery.
410− mResult <- receiveWait
411− [ match (return . Right)
412− , matchIf (\(ProcessMonitorNotification ref _ _) -> ref == mRef)
413− (\(ProcessMonitorNotification _ _ reason) -> return (Left reason))
414− ]
415− case mResult of
416− Right a -> do
417− -- Wait for the monitor message so that we the mailbox doesn't grow
418− receiveWait
419− [ matchIf (\(ProcessMonitorNotification ref _ _) -> ref == mRef)
420− (\(ProcessMonitorNotification {}) -> return ())
421− ]
422− -- Clean up connection to pid
423− reconnect pid
424− return a
425− Left err ->
426− fail $ "call: remote process died: " ++ show err
427−
428−-- | Spawn a child process, have the child link to the parent and the parent
429−-- monitor the child
430−spawnSupervised :: NodeId
431− -> Closure (Process ())
432− -> Process (ProcessId, MonitorRef)
433−spawnSupervised nid proc = do
434− us <- getSelfPid
435− them <- spawn nid (cpLink us `seqCP` proc)
436− ref <- monitor them
437− return (them, ref)
438−
439−-- | Spawn a new process, supplying it with a new 'ReceivePort' and return
440−-- the corresponding 'SendPort'.
441−spawnChannel :: forall a. Typeable a => Static (SerializableDict a)
442− -> NodeId
443− -> Closure (ReceivePort a -> Process ())
444− -> Process (SendPort a)
445−spawnChannel dict nid proc = do
446− us <- getSelfPid
447− _ <- spawn nid (go us)
448− expect
449− where
450− go :: ProcessId -> Closure (Process ())
451− go pid = cpNewChan dict
452− `bindCP`
453− (cpSend (sdictSendPort dict) pid `splitCP` proc)
454− `bindCP`
455− (idCP `closureCompose` staticClosure sndStatic)
456−
457356--------------------------------------------------------------------------------
458357-- Local versions of spawn --
459358--------------------------------------------------------------------------------
@@ -480,3 +379,4 @@ spawnChannelLocal proc = do
480379 liftIO $ putMVar mvar sport
481380 proc rport
482381 takeMVar mvar
382+
src/Control/Distributed/Process/Closure.hs+2
@@ -173,6 +173,7 @@ module Control.Distributed.Process.Closure
173173 -- * CP versions of Cloud Haskell primitives
174174 , cpLink
175175 , cpUnlink
176+ , cpRelay
176177 , cpSend
177178 , cpExpect
178179 , cpNewChan
@@ -205,6 +206,7 @@ import Control.Distributed.Process.Internal.Closure.BuiltIn
205206 -- CP versions of Cloud Haskell primitives
206207 , cpLink
207208 , cpUnlink
209+ , cpRelay
208210 , cpSend
209211 , cpExpect
210212 , cpNewChan
src/Control/Distributed/Process/Debug.hs+45−131
@@ -10,24 +10,25 @@
1010--
1111-- [Tracing/Debugging Facilities]
1212--
13−-- Cloud Haskell provides a general purpose tracing mechanism that allows a
14−-- user supplied /tracer process/ to receive messages when certain events
15−-- take place. It's possible to use this facility to debug a running system
16−-- and/or generate runtime monitoring data in a variety of formats.
13+-- Cloud Haskell provides a general purpose tracing mechanism, allowing a
14+-- user supplied /tracer process/ to receive messages when certain classes of
15+-- system events occur. It's possible to use this facility to aid in debugging
16+-- and/or perform other diagnostic tasks to a program at runtime.
1717--
1818-- [Enabling Tracing]
1919--
20−-- Tracing is disabled by default, because it carries a (relatively small, but
21−-- real) cost at runtime. To enable any kind of tracing to work, the environment
22−-- variable @DISTRIBUTED_PROCESS_TRACE_ENABLED@ must be set (the contents are
23−-- ignored). When the environment variable /is/ set, the system will generate
24−-- a /trace events/ in various circumstances - see 'TraceEvent' for a list of
25−-- all the published events. A user can additionally publish custom trace events
26−-- in the form of 'TraceEvLog' log messages or pass custom (i.e., completely
27−-- user defined) event data using the 'traceMessage' function. If the
28−-- environment variable is not set, no trace events will ever be published.
20+-- Tracing is disabled by default, because it carries a relatively small, but
21+-- tangible cost. To enable tracing, the environment variable
22+-- @DISTRIBUTED_PROCESS_TRACE_ENABLED@ must be set to some value (the contents
23+-- are ignored). When the environment variable /is/ set, the system will
24+-- generate /trace events/ in various circumstances - see 'TraceEvent' for a
25+-- list of all the published events. A user can additionally publish custom
26+-- trace events in the form of 'TraceEvLog' log messages or pass custom
27+-- (i.e., completely user defined) event data using the 'traceMessage' function.
28+-- If the environment variable is not set, no trace events will ever be
29+-- published.
2930--
30−-- All published trace events are forwarded to a /tracer process/, which can be
31+-- All published traces are forwarded to a /tracer process/, which can be
3132-- specified (and changed) at runtime using 'traceEnable'. Some pre-defined
3233-- tracer processes are provided for conveniently printing to stderr, a log file
3334-- or the GHC eventlog.
@@ -100,16 +101,15 @@ module Control.Distributed.Process.Debug
100101 , enableTrace
101102 , enableTraceAsync
102103 , disableTrace
103− , disableTraceAsync
104104 , withTracer
105105 , withFlags
106106 , getTraceFlags
107107 , setTraceFlags
108108 , setTraceFlagsAsync
109109 , defaultTraceFlags
110− , Tracer.traceOn
111− , Tracer.traceOnly
112− , Tracer.traceOff
110+ , traceOn
111+ , traceOnly
112+ , traceOff
113113 -- * Debugging
114114 , startTracer
115115 , stopTracer
@@ -117,6 +117,10 @@ module Control.Distributed.Process.Debug
117117 , traceLog
118118 , traceLogFmt
119119 , traceMessage
120+ -- * Working with remote nodes
121+ , Remote.remoteTable
122+ , Remote.startTraceRelay
123+ , Remote.setTraceFlagsRemote
120124 -- * Built in tracers
121125 , systemLoggerTracer
122126 , logfileTracer
@@ -129,12 +133,9 @@ import Control.Distributed.Process.Internal.Primitives
129133 ( proxy
130134 , finally
131135 , die
132− , getSelfPid
133− , reregister
134136 , whereis
135137 , send
136138 , newChan
137− , sendChan
138139 , receiveChan
139140 , receiveWait
140141 , matchIf
@@ -143,6 +144,9 @@ import Control.Distributed.Process.Internal.Primitives
143144 , monitor
144145 , say
145146 )
147+import Control.Distributed.Process.Internal.Spawn
148+ ( spawn
149+ )
146150import Control.Distributed.Process.Internal.Types
147151 ( Tracer(..)
148152 , LocalNode(..)
@@ -151,6 +155,7 @@ import Control.Distributed.Process.Internal.Types
151155 , LocalProcess(..)
152156 , SendPort(..)
153157 , ProcessMonitorNotification(..)
158+ , NodeId(..)
154159 )
155160import Control.Distributed.Process.Internal.Trace.Types
156161 ( TraceArg(..)
@@ -165,22 +170,23 @@ import Control.Distributed.Process.Internal.Trace.Tracer
165170 , logfileTracer
166171 , eventLogTracer
167172 )
168−import qualified Control.Distributed.Process.Internal.Trace.Primitives as Tracer
169− ( traceLog
170− , traceLogFmt
171− , traceMessage
173+import Control.Distributed.Process.Internal.Trace.Primitives
174+ ( withRegisteredTracer
172175 , enableTrace
173− , enableTraceSync
176+ , enableTraceAsync
174177 , disableTrace
175− , disableTraceSync
176178 , setTraceFlags
177− , setTraceFlagsSync
179+ , setTraceFlagsAsync
178180 , getTraceFlags
179− , getCurrentTraceClient
181+ , isTracingEnabled
180182 , traceOn
181183 , traceOff
182184 , traceOnly
185+ , traceLog
186+ , traceLogFmt
187+ , traceMessage
183188 )
189+import qualified Control.Distributed.Process.Internal.Trace.Remote as Remote
184190import Control.Distributed.Process.Node
185191import Control.Distributed.Process.Serializable
186192
@@ -222,7 +228,6 @@ withTracer :: forall a.
222228 -> Process a
223229 -> Process (Either SomeException a)
224230withTracer handler proc = do
225− say "starting new tracer"
226231 previous <- whereis "tracer"
227232 tracer <- startTracer handler
228233 finally (try proc)
@@ -234,42 +239,36 @@ withTracer handler proc = do
234239 Nothing -> return ()
235240 Just p -> do
236241 ref <- monitor tracer
237− say "resetting to previous tracer" >> enableTrace p
238− say "stopping tracer"
239242 send tracer TraceEvDisable
240− say $ "waiting for EXIT from tracer " ++ (show tracer)
241243 receiveWait [
242244 matchIf (\(ProcessMonitorNotification ref' _ _) -> ref == ref')
243− (\_ -> say "seen EXIT ok")
245+ (\_ -> return ())
244246 ]
245247
246248-- | Evaluate @proc@ with the supplied flags enabled. Any previously set
247−-- flags are restored immediately afterwards.
249+-- trace flags are restored immediately afterwards.
248250withFlags :: forall a.
249251 TraceFlags
250252 -> Process a
251253 -> Process (Either SomeException a)
252254withFlags flags proc = do
253− (sp, rp) <- newChan
254− withLocalTracer $ \t -> liftIO $ Tracer.getTraceFlags t sp
255− oldFlags <- receiveChan rp
255+ oldFlags <- getTraceFlags
256256 finally (setTraceFlags flags >> try proc)
257− (setTraceFlags oldFlags >> say "exit withFlags")
257+ (setTraceFlags oldFlags)
258258
259259traceProxy :: ProcessId -> (TraceEvent -> Process ()) -> Process ()
260260traceProxy pid act = do
261− say "tracing has started"
262261 proxy pid $ \(ev :: TraceEvent) ->
263262 case ev of
264− (TraceEvTakeover p) -> say ((show p) ++ " has taken over...") >> return False
265− TraceEvDisable -> say "dying - disabled..." >> die "disabled"
266− _ -> say ("act on " ++ (show ev)) >> act ev >> return True
263+ (TraceEvTakeover p) -> return False
264+ TraceEvDisable -> die "disabled"
265+ _ -> act ev >> return True
267266
268267-- | Stops a user supplied tracer started with 'startTracer'.
269268-- Note that only one tracer process can be active at any given time.
270269-- This process will stop the last process started with 'startTracer'.
271270-- If 'startTracer' is called multiple times, successive calls to this
272−-- function will stop the tracers in the reverse order in which they were
271+-- function will stop the tracers in the reverse order which they were
273272-- started.
274273--
275274-- This function will never stop the system tracer (i.e., the tracer
@@ -278,10 +277,8 @@ traceProxy pid act = do
278277-- subsequent calls to this function will have no effect.
279278--
280279-- If tracing support is disabled for the node, this function will also
281−-- have no effect. If the last tracer to have been registered (or the process
282−-- registered with the label @"tracer"@ at the time the stop instruction
283−-- is received) was not started with 'startTracer' then the behaviour of
284−-- this function is /undefined/.
280+-- have no effect. If the last tracer to have been registered was not started
281+-- with 'startTracer' then the behaviour of this function is /undefined/.
285282stopTracer :: Process ()
286283stopTracer =
287284 withRegisteredTracer $ \pid -> do
@@ -293,86 +290,3 @@ stopTracer =
293290 True -> return ()
294291 False -> send pid TraceEvDisable
295292
296−withRegisteredTracer :: (ProcessId -> Process a) -> Process a
297−withRegisteredTracer act = do
298− (sp, rp) <- newChan
299− withLocalTracer $ \t -> liftIO $ Tracer.getCurrentTraceClient t sp
300− currentTracer <- receiveChan rp
301− case currentTracer of
302− Nothing -> do { (Just p') <- whereis "tracer.initial"; act p' }
303− (Just p) -> act p
304−
305−-- | Determine whether or not tracing has been enabled.
306−isTracingEnabled :: Process Bool
307−isTracingEnabled = do
308− node <- processNode <$> ask
309− case (localTracer node) of
310− (ActiveTracer _ _) -> return True
311− _ -> return False
312−
313−-- | Enable tracing to the supplied process.
314−enableTraceAsync :: ProcessId -> Process ()
315−enableTraceAsync pid = withLocalTracer $ \t -> liftIO $ Tracer.enableTrace t pid
316−
317−-- TODO: refactor _Sync versions of trace configuration functions...
318−
319−-- | Enable tracing to the supplied process and wait for a @TraceOk@
320−-- response from the trace coordinator process.
321−enableTrace :: ProcessId -> Process ()
322−enableTrace pid =
323− withLocalTracerSync $ \t sp -> Tracer.enableTraceSync t sp pid
324−
325−-- | Disable the currently configured trace.
326−disableTraceAsync :: Process ()
327−disableTraceAsync = withLocalTracer $ \t -> liftIO $ Tracer.disableTrace t
328−
329−-- | Disable the currently configured trace and wait for a @TraceOk@
330−-- response from the trace coordinator process.
331−disableTrace :: Process ()
332−disableTrace =
333− withLocalTracerSync $ \t sp -> Tracer.disableTraceSync t sp
334−
335−getTraceFlags :: Process TraceFlags
336−getTraceFlags = do
337− (sp, rp) <- newChan
338− withLocalTracer $ \t -> liftIO $ Tracer.getTraceFlags t sp
339− receiveChan rp
340−
341−-- | Set the given flags for the current tracer.
342−setTraceFlagsAsync :: TraceFlags -> Process ()
343−setTraceFlagsAsync f = withLocalTracer $ \t -> liftIO $ Tracer.setTraceFlags t f
344−
345−-- | Set the given flags for the current tracer and wait for a @TraceOk@
346−-- response from the trace coordinator process.
347−setTraceFlags :: TraceFlags -> Process ()
348−setTraceFlags f =
349− withLocalTracerSync $ \t sp -> Tracer.setTraceFlagsSync t sp f
350−
351−withLocalTracerSync :: (Tracer -> SendPort TraceOk -> IO ()) -> Process ()
352−withLocalTracerSync act = do
353− (sp, rp) <- newChan
354− withLocalTracer $ \t -> liftIO $ (act t sp)
355− TraceOk <- receiveChan rp
356− return ()
357−
358−-- | Send a log message to the internal tracing facility. If tracing is
359−-- enabled, this will create a custom trace log event.
360−--
361−traceLog :: String -> Process ()
362−traceLog s = withLocalTracer $ \t -> liftIO $ Tracer.traceLog t s
363−
364−-- | Send a log message to the internal tracing facility, using the given
365−-- list of printable 'TraceArg's interspersed with the preceding delimiter.
366−--
367−traceLogFmt :: String -> [TraceArg] -> Process ()
368−traceLogFmt d ls = withLocalTracer $ \t -> liftIO $ Tracer.traceLogFmt t d ls
369−
370−-- | Send an arbitrary 'Message' to the tracer process.
371−traceMessage :: Serializable m => m -> Process ()
372−traceMessage msg = withLocalTracer $ \t -> liftIO $ Tracer.traceMessage t msg
373−
374−withLocalTracer :: (Tracer -> Process ()) -> Process ()
375−withLocalTracer act = do
376− node <- processNode <$> ask
377− act (localTracer node)
378−
src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs+24
@@ -16,13 +16,16 @@ module Control.Distributed.Process.Internal.Closure.BuiltIn
1616 , bindCP
1717 , seqCP
1818 -- * CP versions of Cloud Haskell primitives
19+ , decodeProcessIdStatic
1920 , cpLink
2021 , cpUnlink
22+ , cpRelay
2123 , cpSend
2224 , cpExpect
2325 , cpNewChan
2426 -- * Support for some CH operations
2527 , cpDelay
28+ , cpEnableTraceRemote
2629 ) where
2730
2831import Data.ByteString.Lazy (ByteString)
@@ -56,6 +59,7 @@ import Control.Distributed.Process.Internal.Types
5659import Control.Distributed.Process.Internal.Primitives
5760 ( link
5861 , unlink
62+ , relay
5963 , send
6064 , expect
6165 , newChan
@@ -82,6 +86,7 @@ remoteTable =
8286 . registerStatic "$decodeProcessId" (toDynamic (decode :: ByteString -> ProcessId))
8387 . registerStatic "$link" (toDynamic link)
8488 . registerStatic "$unlink" (toDynamic unlink)
89+ . registerStatic "$relay" (toDynamic relay)
8590 . registerStatic "$sendDict" (toDynamic (sendDict :: SerializableDict ANY -> ProcessId -> ANY -> Process ()))
8691 . registerStatic "$expectDict" (toDynamic (expectDict :: SerializableDict ANY -> Process ANY))
8792 . registerStatic "$newChanDict" (toDynamic (newChanDict :: SerializableDict ANY -> Process (SendPort ANY, ReceivePort ANY)))
@@ -246,6 +251,22 @@ cpNewChan dict = staticClosure (newChanDictStatic `staticApply` dict)
246251 => Static (SerializableDict a -> Process (SendPort a, ReceivePort a))
247252 newChanDictStatic = staticLabel "$newChanDict"
248253
254+-- | 'CP' version of 'relay'
255+cpRelay :: ProcessId -> Closure (Process ())
256+cpRelay = closure (relayStatic `staticCompose` decodeProcessIdStatic) . encode
257+ where
258+ relayStatic :: Static (ProcessId -> Process ())
259+ relayStatic = staticLabel "$relay"
260+
261+-- TODO: move cpEnableTraceRemote into Trace/Primitives.hs
262+
263+cpEnableTraceRemote :: ProcessId -> Closure (Process ())
264+cpEnableTraceRemote =
265+ closure (enableTraceStatic `staticCompose` decodeProcessIdStatic) . encode
266+ where
267+ enableTraceStatic :: Static (ProcessId -> Process ())
268+ enableTraceStatic = staticLabel "$enableTraceRemote"
269+
249270--------------------------------------------------------------------------------
250271-- Support for spawn --
251272--------------------------------------------------------------------------------
@@ -275,3 +296,6 @@ cpDelay = closureApply . cpDelay'
275296
276297 delayStatic :: Static (ProcessId -> Process () -> Process ())
277298 delayStatic = staticLabel "$delay"
299+
300+
301+
src/Control/Distributed/Process/Internal/Primitives.hs+20−7
@@ -1,3 +1,5 @@
1+{-# LANGUAGE BangPatterns #-}
2+
13-- | Cloud Haskell primitives
24--
35-- We define these in a separate module so that we don't have to rely on
@@ -131,7 +133,19 @@ import Control.Distributed.Process.Internal.CQueue
131133 )
132134import Control.Distributed.Process.Serializable (Serializable, fingerprint)
133135import Data.Accessor ((^.), (^:), (^=))
134−import Control.Distributed.Static (Closure, Static)
136+import Control.Distributed.Static
137+ ( RemoteTable
138+ , registerStatic
139+ , Static
140+ , staticLabel
141+ , staticApply
142+ , Closure
143+ , closure
144+ , closureApplyStatic
145+ , closureApply
146+ , staticCompose
147+ , staticClosure
148+ )
135149import Data.Rank1Typeable (Typeable)
136150import qualified Control.Distributed.Static as Static (unstatic, unclosure)
137151import Control.Distributed.Process.Internal.Types
@@ -178,7 +192,7 @@ import Control.Distributed.Process.Internal.Messaging
178192 , sendPayload
179193 , disconnect
180194 )
181−import Control.Distributed.Process.Internal.Trace.Primitives
195+import Control.Distributed.Process.Internal.Trace.Types
182196 ( traceEvent
183197 , TraceEvent(..)
184198 )
@@ -516,7 +530,7 @@ delegate pid p = do
516530
517531-- | A straight relay that simply forwards all messages to the supplied pid.
518532relay :: ProcessId -> Process ()
519−relay pid = receiveWait [ matchAny (\m -> forward m pid) ] >> relay pid
533+relay !pid = receiveWait [ matchAny (\m -> forward m pid) ] >> relay pid
520534
521535-- | Proxies @pid@ and forwards messages whenever @proc@ evaluates to @True@.
522536-- Unlike 'delegate' the predicate @proc@ runs in the 'Process' monad, allowing
@@ -526,12 +540,11 @@ proxy :: Serializable a => ProcessId -> (a -> Process Bool) -> Process ()
526540proxy pid proc = do
527541 receiveWait [
528542 matchAny (\m -> do
529− say "handling proxied message..."
530543 next <- handleMessage m proc
531544 case next of
532− Just True -> say ("forwarding msg to " ++ (show pid)) >> forward m pid
533− Just False -> say "ignoring explicitly ignored msg"
534− Nothing -> say "ignoring unroutable msg")
545+ Just True -> forward m pid
546+ Just False -> return () -- explicitly ignored
547+ Nothing -> return ()) -- un-routable
535548 ]
536549 proxy pid proc
537550
src/Control/Distributed/Process/Internal/Spawn.hsnew file+168
@@ -0,0 +1,168 @@
1+module Control.Distributed.Process.Internal.Spawn
2+ ( spawn
3+ , spawnLink
4+ , spawnMonitor
5+ , call
6+ , spawnSupervised
7+ , spawnChannel
8+ ) where
9+
10+import Data.Typeable (Typeable)
11+import Control.Monad.IO.Class (liftIO)
12+import Control.Applicative ((<$>))
13+import Control.Monad.Reader (ask)
14+import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
15+import Control.Distributed.Static
16+ ( Static
17+ , Closure
18+ , closureCompose
19+ , staticClosure
20+ )
21+import Control.Distributed.Process.Internal.Types
22+ ( NodeId(..)
23+ , ProcessId(..)
24+ , Process(..)
25+ , MonitorRef(..)
26+ , ProcessMonitorNotification(..)
27+ , NodeMonitorNotification(..)
28+ , DidSpawn(..)
29+ , SendPort(..)
30+ , ReceivePort(..)
31+ , LocalProcess(processNode)
32+ , nullProcessId
33+ )
34+import Control.Distributed.Process.Serializable (Serializable, SerializableDict)
35+import Control.Distributed.Process.Internal.Closure.BuiltIn
36+ ( sdictSendPort
37+ , sndStatic
38+ , idCP
39+ , seqCP
40+ , bindCP
41+ , splitCP
42+ , cpLink
43+ , cpSend
44+ , cpNewChan
45+ , cpDelay
46+ )
47+import Control.Distributed.Process.Internal.Primitives
48+ ( -- Basic messaging
49+ send
50+ , expect
51+ , receiveWait
52+ , match
53+ , matchIf
54+ , link
55+ , getSelfPid
56+ , monitor
57+ , monitorNode
58+ , unmonitor
59+ , spawnAsync
60+ , reconnect
61+ , newChan
62+ )
63+
64+-- | Spawn a process
65+--
66+-- For more information about 'Closure', see
67+-- "Control.Distributed.Process.Closure".
68+--
69+-- See also 'call'.
70+spawn :: NodeId -> Closure (Process ()) -> Process ProcessId
71+spawn nid proc = do
72+ us <- getSelfPid
73+ mRef <- monitorNode nid
74+ sRef <- spawnAsync nid (cpDelay us proc)
75+ receiveWait [
76+ matchIf (\(DidSpawn ref _) -> ref == sRef) $ \(DidSpawn _ pid) -> do
77+ unmonitor mRef
78+ send pid ()
79+ return pid
80+ , matchIf (\(NodeMonitorNotification ref _ _) -> ref == mRef) $ \_ ->
81+ return (nullProcessId nid)
82+ ]
83+
84+-- | Spawn a process and link to it
85+--
86+-- Note that this is just the sequential composition of 'spawn' and 'link'.
87+-- (The "Unified" semantics that underlies Cloud Haskell does not even support
88+-- a synchronous link operation)
89+spawnLink :: NodeId -> Closure (Process ()) -> Process ProcessId
90+spawnLink nid proc = do
91+ pid <- spawn nid proc
92+ link pid
93+ return pid
94+
95+-- | Like 'spawnLink', but monitor the spawned process
96+spawnMonitor :: NodeId -> Closure (Process ()) -> Process (ProcessId, MonitorRef)
97+spawnMonitor nid proc = do
98+ pid <- spawn nid proc
99+ ref <- monitor pid
100+ return (pid, ref)
101+
102+-- | Run a process remotely and wait for it to reply
103+--
104+-- We monitor the remote process: if it dies before it can send a reply, we die
105+-- too.
106+--
107+-- For more information about 'Static', 'SerializableDict', and 'Closure', see
108+-- "Control.Distributed.Process.Closure".
109+--
110+-- See also 'spawn'.
111+call :: Serializable a
112+ => Static (SerializableDict a)
113+ -> NodeId
114+ -> Closure (Process a)
115+ -> Process a
116+call dict nid proc = do
117+ us <- getSelfPid
118+ (pid, mRef) <- spawnMonitor nid (proc `bindCP` cpSend dict us)
119+ -- We are guaranteed to receive the reply before the monitor notification
120+ -- (if a reply is sent at all)
121+ -- NOTE: This might not be true if we switch to unreliable delivery.
122+ mResult <- receiveWait
123+ [ match (return . Right)
124+ , matchIf (\(ProcessMonitorNotification ref _ _) -> ref == mRef)
125+ (\(ProcessMonitorNotification _ _ reason) -> return (Left reason))
126+ ]
127+ case mResult of
128+ Right a -> do
129+ -- Wait for the monitor message so that we the mailbox doesn't grow
130+ receiveWait
131+ [ matchIf (\(ProcessMonitorNotification ref _ _) -> ref == mRef)
132+ (\(ProcessMonitorNotification {}) -> return ())
133+ ]
134+ -- Clean up connection to pid
135+ reconnect pid
136+ return a
137+ Left err ->
138+ fail $ "call: remote process died: " ++ show err
139+
140+-- | Spawn a child process, have the child link to the parent and the parent
141+-- monitor the child
142+spawnSupervised :: NodeId
143+ -> Closure (Process ())
144+ -> Process (ProcessId, MonitorRef)
145+spawnSupervised nid proc = do
146+ us <- getSelfPid
147+ them <- spawn nid (cpLink us `seqCP` proc)
148+ ref <- monitor them
149+ return (them, ref)
150+
151+-- | Spawn a new process, supplying it with a new 'ReceivePort' and return
152+-- the corresponding 'SendPort'.
153+spawnChannel :: forall a. Typeable a => Static (SerializableDict a)
154+ -> NodeId
155+ -> Closure (ReceivePort a -> Process ())
156+ -> Process (SendPort a)
157+spawnChannel dict nid proc = do
158+ us <- getSelfPid
159+ _ <- spawn nid (go us)
160+ expect
161+ where
162+ go :: ProcessId -> Closure (Process ())
163+ go pid = cpNewChan dict
164+ `bindCP`
165+ (cpSend (sdictSendPort dict) pid `splitCP` proc)
166+ `bindCP`
167+ (idCP `closureCompose` staticClosure sndStatic)
168+
src/Control/Distributed/Process/Internal/Trace/Primitives.hs+144−68
@@ -1,30 +1,52 @@
11-- | Keeps the tracing API calls separate from the Tracer implementation,
22-- which allows us to avoid a nasty import cycle between tracing and
3−-- the messaging primitives that rely on it
3+-- the messaging primitives that rely on it, and also between the node
4+-- controller (which requires access to the tracing related elements of
5+-- our RemoteTable) and the Debug module, which requires @forkProcess@.
46module Control.Distributed.Process.Internal.Trace.Primitives
57 ( Tracer
68 , TraceEvent(..)
79 -- * Sending Trace Data
810 , traceLog
911 , traceLogFmt
10− , traceEvent
1112 , traceMessage
1213 -- * Configuring A Tracer
14+ , isTracingEnabled
1315 , defaultTraceFlags
1416 , enableTrace
15− , enableTraceSync
17+ , enableTraceAsync
1618 , disableTrace
17− , disableTraceSync
19+ , disableTraceAsync
1820 , getTraceFlags
1921 , setTraceFlags
20− , setTraceFlagsSync
21− , getCurrentTraceClient
22+ , setTraceFlagsAsync
2223 , traceOnly
2324 , traceOn
2425 , traceOff
26+ , withLocalTracer
27+ , withRegisteredTracer
2528 ) where
2629
27−import Control.Distributed.Process.Internal.CQueue (enqueue)
30+import Control.Applicative ((<$>))
31+import Control.Distributed.Process.Internal.Closure.BuiltIn
32+ ( cpEnableTraceRemote
33+ )
34+import Control.Distributed.Process.Internal.Primitives
35+ ( proxy
36+ , finally
37+ , die
38+ , whereis
39+ , send
40+ , newChan
41+ , receiveChan
42+ , receiveWait
43+ , matchIf
44+ , finally
45+ , try
46+ , monitor
47+ , getSelfPid
48+ , relay
49+ )
2850import Control.Distributed.Process.Internal.Trace.Types
2951 ( TraceArg(..)
3052 , TraceEvent(..)
@@ -34,76 +56,50 @@ import Control.Distributed.Process.Internal.Trace.Types
3456 , TraceSubject(..)
3557 , defaultTraceFlags
3658 )
59+import qualified Control.Distributed.Process.Internal.Trace.Types as Tracer
60+ ( traceLog
61+ , traceLogFmt
62+ , traceMessage
63+ , enableTrace
64+ , enableTraceSync
65+ , disableTrace
66+ , disableTraceSync
67+ , setTraceFlags
68+ , setTraceFlagsSync
69+ , getTraceFlags
70+ , getCurrentTraceClient
71+ )
72+import Control.Distributed.Process.Internal.Spawn
73+ ( spawn
74+ )
3775import Control.Distributed.Process.Internal.Types
3876 ( Tracer(..)
77+ , Process
3978 , ProcessId
79+ , LocalProcess(..)
80+ , LocalNode(localTracer)
4081 , Message
4182 , SendPort
83+ , NodeId
4284 , createUnencodedMessage
4385 )
4486import Control.Distributed.Process.Serializable
45−import Data.Foldable (forM_)
46−import Data.List (intersperse)
87+import Control.Distributed.Static
88+ ( RemoteTable
89+ , registerStatic
90+ )
91+import Control.Monad.IO.Class (liftIO)
92+import Control.Monad.Reader (ask)
93+
94+import Data.Rank1Dynamic (toDynamic)
4795import qualified Data.Set as Set (fromList)
48−import System.Mem.Weak (deRefWeak)
4996
5097--------------------------------------------------------------------------------
5198-- Main API --
5299--------------------------------------------------------------------------------
53100
54−traceLog :: Tracer -> String -> IO ()
55−traceLog tr s = traceMessage tr (TraceEvLog s)
56−
57−traceLogFmt :: Tracer
58− -> String
59− -> [TraceArg]
60− -> IO ()
61−traceLogFmt t d ls =
62− traceLog t $ concat (intersperse d (map toS ls))
63− where toS :: TraceArg -> String
64− toS (TraceStr s) = s
65− toS (Trace a) = show a
66−
67−traceEvent :: Tracer -> TraceEvent -> IO ()
68−traceEvent tr ev = traceIt tr (createUnencodedMessage ev)
69−
70−traceMessage :: Serializable m => Tracer -> m -> IO ()
71−traceMessage tr msg = traceEvent tr (TraceEvUser (createUnencodedMessage msg))
72−
73−enableTrace :: Tracer -> ProcessId -> IO ()
74−enableTrace t p =
75− traceIt t (createUnencodedMessage ((Nothing :: Maybe (SendPort TraceOk)),
76− (TraceEnable p)))
77−
78−enableTraceSync :: Tracer -> SendPort TraceOk -> ProcessId -> IO ()
79−enableTraceSync t s p =
80− traceIt t (createUnencodedMessage ((Just s), (TraceEnable p)))
81−
82−disableTrace :: Tracer -> IO ()
83−disableTrace t =
84− traceIt t (createUnencodedMessage ((Nothing :: Maybe (SendPort TraceOk)),
85− TraceDisable))
86−
87−disableTraceSync :: Tracer -> SendPort TraceOk -> IO ()
88−disableTraceSync t s =
89− traceIt t (createUnencodedMessage ((Just s), TraceDisable))
90−
91−setTraceFlags :: Tracer -> TraceFlags -> IO ()
92−setTraceFlags t f =
93− traceIt t (createUnencodedMessage ((Nothing :: Maybe (SendPort TraceOk)), f))
94−
95−setTraceFlagsSync :: Tracer -> SendPort TraceOk -> TraceFlags -> IO ()
96−setTraceFlagsSync t s f =
97− traceIt t (createUnencodedMessage ((Just s), f))
98−
99−getTraceFlags :: Tracer -> SendPort TraceFlags -> IO ()
100−getTraceFlags t s = traceIt t (createUnencodedMessage s)
101−
102−getCurrentTraceClient :: Tracer -> SendPort (Maybe ProcessId) -> IO ()
103−getCurrentTraceClient t s = traceIt t (createUnencodedMessage s)
104−
105−-- aux API and utilities
106−
101+-- | Maps a list of identifiers that can be mapped to process ids,
102+-- to a 'TraceSubject'.
107103class Traceable a where
108104 uod :: [a] -> TraceSubject
109105
@@ -113,18 +109,98 @@ instance Traceable ProcessId where
113109instance Traceable String where
114110 uod = TraceNames . Set.fromList
115111
112+-- | Turn tracing for for a subset of trace targets.
116113traceOnly :: Traceable a => [a] -> Maybe TraceSubject
117114traceOnly = Just . uod
118115
116+-- | Trace all targets.
119117traceOn :: Maybe TraceSubject
120118traceOn = Just TraceAll
121119
120+-- | Trace no targets.
122121traceOff :: Maybe TraceSubject
123122traceOff = Nothing
124123
125−traceIt :: Tracer -> Message -> IO ()
126−traceIt InactiveTracer _ = return ()
127−traceIt (ActiveTracer _ wqRef) msg = do
128− mQueue <- deRefWeak wqRef
129− forM_ mQueue $ \queue -> enqueue queue msg
124+-- | Determine whether or not tracing has been enabled.
125+isTracingEnabled :: Process Bool
126+isTracingEnabled = do
127+ node <- processNode <$> ask
128+ case (localTracer node) of
129+ (ActiveTracer _ _) -> return True
130+ _ -> return False
131+
132+-- | Enable tracing to the supplied process.
133+enableTraceAsync :: ProcessId -> Process ()
134+enableTraceAsync pid = withLocalTracer $ \t -> liftIO $ Tracer.enableTrace t pid
135+
136+-- TODO: refactor _Sync versions of trace configuration functions...
137+
138+-- | Enable tracing to the supplied process and wait for a @TraceOk@
139+-- response from the trace coordinator process.
140+enableTrace :: ProcessId -> Process ()
141+enableTrace pid =
142+ withLocalTracerSync $ \t sp -> Tracer.enableTraceSync t sp pid
143+
144+-- | Disable the currently configured trace.
145+disableTraceAsync :: Process ()
146+disableTraceAsync = withLocalTracer $ \t -> liftIO $ Tracer.disableTrace t
147+
148+-- | Disable the currently configured trace and wait for a @TraceOk@
149+-- response from the trace coordinator process.
150+disableTrace :: Process ()
151+disableTrace =
152+ withLocalTracerSync $ \t sp -> Tracer.disableTraceSync t sp
153+
154+getTraceFlags :: Process TraceFlags
155+getTraceFlags = do
156+ (sp, rp) <- newChan
157+ withLocalTracer $ \t -> liftIO $ Tracer.getTraceFlags t sp
158+ receiveChan rp
159+
160+-- | Set the given flags for the current tracer.
161+setTraceFlagsAsync :: TraceFlags -> Process ()
162+setTraceFlagsAsync f = withLocalTracer $ \t -> liftIO $ Tracer.setTraceFlags t f
163+
164+-- | Set the given flags for the current tracer and wait for a @TraceOk@
165+-- response from the trace coordinator process.
166+setTraceFlags :: TraceFlags -> Process ()
167+setTraceFlags f =
168+ withLocalTracerSync $ \t sp -> Tracer.setTraceFlagsSync t sp f
169+
170+-- | Send a log message to the internal tracing facility. If tracing is
171+-- enabled, this will create a custom trace log event.
172+--
173+traceLog :: String -> Process ()
174+traceLog s = withLocalTracer $ \t -> liftIO $ Tracer.traceLog t s
175+
176+-- | Send a log message to the internal tracing facility, using the given
177+-- list of printable 'TraceArg's interspersed with the preceding delimiter.
178+--
179+traceLogFmt :: String -> [TraceArg] -> Process ()
180+traceLogFmt d ls = withLocalTracer $ \t -> liftIO $ Tracer.traceLogFmt t d ls
181+
182+-- | Send an arbitrary 'Message' to the tracer process.
183+traceMessage :: Serializable m => m -> Process ()
184+traceMessage msg = withLocalTracer $ \t -> liftIO $ Tracer.traceMessage t msg
185+
186+withLocalTracer :: (Tracer -> Process ()) -> Process ()
187+withLocalTracer act = do
188+ node <- processNode <$> ask
189+ act (localTracer node)
190+
191+withLocalTracerSync :: (Tracer -> SendPort TraceOk -> IO ()) -> Process ()
192+withLocalTracerSync act = do
193+ (sp, rp) <- newChan
194+ withLocalTracer $ \t -> liftIO $ (act t sp)
195+ TraceOk <- receiveChan rp
196+ return ()
197+
198+withRegisteredTracer :: (ProcessId -> Process a) -> Process a
199+withRegisteredTracer act = do
200+ (sp, rp) <- newChan
201+ withLocalTracer $ \t -> liftIO $ Tracer.getCurrentTraceClient t sp
202+ currentTracer <- receiveChan rp
203+ case currentTracer of
204+ Nothing -> do { (Just p') <- whereis "tracer.initial"; act p' }
205+ (Just p) -> act p
130206
src/Control/Distributed/Process/Internal/Trace/Remote.hsnew file+80
@@ -0,0 +1,80 @@
1+module Control.Distributed.Process.Internal.Trace.Remote
2+ ( -- * Configuring A Tracer
3+ setTraceFlagsRemote
4+ , startTraceRelay
5+ -- * Remote Table
6+ , remoteTable
7+ ) where
8+
9+import Control.Applicative ((<$>))
10+import Control.Distributed.Process.Internal.Closure.BuiltIn
11+ ( cpEnableTraceRemote
12+ )
13+import Control.Distributed.Process.Internal.Primitives
14+ ( proxy
15+ , finally
16+ , die
17+ , whereis
18+ , send
19+ , newChan
20+ , receiveChan
21+ , receiveWait
22+ , matchIf
23+ , finally
24+ , try
25+ , monitor
26+ , getSelfPid
27+ , relay
28+ , nsendRemote
29+ , say
30+ )
31+import Control.Distributed.Process.Internal.Trace.Types
32+ ( TraceFlags(..)
33+ , TraceOk(..)
34+ , TraceSubject(..)
35+ , defaultTraceFlags
36+ )
37+import Control.Distributed.Process.Internal.Trace.Primitives
38+ ( withRegisteredTracer
39+ , enableTrace
40+ )
41+import Control.Distributed.Process.Internal.Spawn
42+ ( spawn
43+ )
44+import Control.Distributed.Process.Internal.Types
45+ ( Process
46+ , ProcessId
47+ , SendPort
48+ , NodeId
49+ )
50+import Control.Distributed.Process.Serializable
51+import Control.Distributed.Static
52+ ( RemoteTable
53+ , registerStatic
54+ )
55+import Data.Rank1Dynamic (toDynamic)
56+import qualified Data.Set as Set (fromList)
57+
58+-- | Remote Table.
59+remoteTable :: RemoteTable -> RemoteTable
60+remoteTable = registerStatic "$enableTraceRemote" (toDynamic enableTraceRemote)
61+
62+enableTraceRemote :: ProcessId -> Process ()
63+enableTraceRemote pid =
64+ getSelfPid >>= enableTrace >> relay pid
65+
66+-- | Starts a /trace relay/ process on the remote node, that forwards all trace
67+-- events to the registered tracer on /this/ (the calling process') node.
68+startTraceRelay :: NodeId -> Process ProcessId
69+startTraceRelay nodeId = do
70+ withRegisteredTracer $ \pid ->
71+ -- TODO: move cpEnableTraceRemote here?
72+ spawn nodeId $ cpEnableTraceRemote pid
73+
74+-- | Set the given flags for a remote node (asynchronous).
75+setTraceFlagsRemote :: TraceFlags -> NodeId -> Process ()
76+setTraceFlagsRemote flags node = do
77+ nsendRemote node
78+ "trace.controller"
79+ ((Nothing :: Maybe (SendPort TraceOk)), flags)
80+
src/Control/Distributed/Process/Internal/Trace/Tracer.hs+6−5
@@ -20,6 +20,7 @@ import Control.Distributed.Process.Internal.CQueue
2020 )
2121import Control.Distributed.Process.Internal.Primitives
2222 ( catch
23+ , die
2324 , receiveWait
2425 , forward
2526 , sendChan
@@ -29,7 +30,9 @@ import Control.Distributed.Process.Internal.Primitives
2930 , handleMessage
3031 , matchUnknown
3132 , whereis
32− , reregister, say, unwrapMessage
33+ , getSelfPid
34+ , register
35+ , say, unwrapMessage
3336 )
3437import Control.Distributed.Process.Internal.Trace.Types
3538 ( TraceEvent(..)
@@ -156,6 +159,7 @@ eventLogTracer = do
156159logfileTracer :: FilePath -> Process ()
157160logfileTracer p = do
158161 -- TODO: error handling if the handle cannot be opened
162+ liftIO $ putStrLn $ "writing to " ++ (show p)
159163 h <- liftIO $ openFile p AppendMode
160164 liftIO $ hSetBuffering h LineBuffering
161165 logger h `catch` (\(_ :: SomeException) -> liftIO $ hClose h)
@@ -167,7 +171,7 @@ logfileTracer p = do
167171 TraceEvDisable -> True
168172 (TraceEvTakeover _) -> True
169173 _ -> False)
170− (\_ -> (liftIO $ hClose h') >> error "trace stopped")
174+ (\_ -> (liftIO $ hClose h') >> die "trace stopped")
171175 , matchAny (\ev -> handleMessage ev (writeTrace h'))
172176 ]
173177
@@ -207,13 +211,11 @@ traceController mv = do
207211 (TraceEnable pid) -> do
208212 -- notify the previous tracer it has been replaced
209213 sendTrace st (createUnencodedMessage (TraceEvTakeover pid))
210− say $ "registering " ++ (show pid) ++ " as new tracer"
211214 sendOk setResp
212215 return st { client = (Just pid) }
213216 TraceDisable -> do
214217 sendTrace st (createUnencodedMessage TraceEvDisable)
215218 sendOk setResp
216− say "setting sendTrace to noop"
217219 return st { client = Nothing })
218220 , match (\(confResp, flags') ->
219221 sendOk confResp >> applyTraceFlags flags' st)
@@ -327,7 +329,6 @@ sendTrace :: TracerState -> Message -> Process ()
327329sendTrace st msg =
328330 let pid = (client st) in do
329331 (Just ev) <- unwrapMessage msg :: Process (Maybe TraceEvent)
330− say ("sending " ++ (show ev) ++ " to " ++ (show pid))
331332 case pid of
332333 Just p -> (flip forward) p msg
333334 Nothing -> return ()
src/Control/Distributed/Process/Internal/Trace/Types.hs+100
@@ -9,19 +9,40 @@ module Control.Distributed.Process.Internal.Trace.Types
99 , defaultTraceFlags
1010 , TraceArg(..)
1111 , TraceOk(..)
12+ , traceLog
13+ , traceLogFmt
14+ , traceEvent
15+ , traceMessage
16+ , defaultTraceFlags
17+ , enableTrace
18+ , enableTraceSync
19+ , disableTrace
20+ , disableTraceSync
21+ , getTraceFlags
22+ , setTraceFlags
23+ , setTraceFlagsSync
24+ , getCurrentTraceClient
1225 ) where
1326
1427import Control.Applicative ((<$>), (<*>))
28+import Control.Distributed.Process.Internal.CQueue (enqueue)
1529import Control.Distributed.Process.Internal.Types
1630 ( Tracer(..)
1731 , NodeId
1832 , ProcessId
1933 , DiedReason
2034 , Message
35+ , SendPort
36+ , createUnencodedMessage
2137 )
38+import Control.Distributed.Process.Serializable
2239import Data.Binary
40+import Data.Foldable (forM_)
41+import Data.List (intersperse)
2342import Data.Set (Set)
43+import qualified Data.Set as Set (fromList)
2444import Data.Typeable
45+import System.Mem.Weak (deRefWeak)
2546import Network.Transport
2647 ( ConnectionId
2748 )
@@ -120,6 +141,85 @@ data TraceArg =
120141data TraceOk = TraceOk
121142 deriving (Typeable)
122143
144+--------------------------------------------------------------------------------
145+-- Internal/Common API --
146+--------------------------------------------------------------------------------
147+
148+traceLog :: Tracer -> String -> IO ()
149+traceLog tr s = traceMessage tr (TraceEvLog s)
150+
151+traceLogFmt :: Tracer
152+ -> String
153+ -> [TraceArg]
154+ -> IO ()
155+traceLogFmt t d ls =
156+ traceLog t $ concat (intersperse d (map toS ls))
157+ where toS :: TraceArg -> String
158+ toS (TraceStr s) = s
159+ toS (Trace a) = show a
160+
161+traceEvent :: Tracer -> TraceEvent -> IO ()
162+traceEvent tr ev = traceIt tr (createUnencodedMessage ev)
163+
164+traceMessage :: Serializable m => Tracer -> m -> IO ()
165+traceMessage tr msg = traceEvent tr (TraceEvUser (createUnencodedMessage msg))
166+
167+enableTrace :: Tracer -> ProcessId -> IO ()
168+enableTrace t p =
169+ traceIt t (createUnencodedMessage ((Nothing :: Maybe (SendPort TraceOk)),
170+ (TraceEnable p)))
171+
172+enableTraceSync :: Tracer -> SendPort TraceOk -> ProcessId -> IO ()
173+enableTraceSync t s p =
174+ traceIt t (createUnencodedMessage ((Just s), (TraceEnable p)))
175+
176+disableTrace :: Tracer -> IO ()
177+disableTrace t =
178+ traceIt t (createUnencodedMessage ((Nothing :: Maybe (SendPort TraceOk)),
179+ TraceDisable))
180+
181+disableTraceSync :: Tracer -> SendPort TraceOk -> IO ()
182+disableTraceSync t s =
183+ traceIt t (createUnencodedMessage ((Just s), TraceDisable))
184+
185+setTraceFlags :: Tracer -> TraceFlags -> IO ()
186+setTraceFlags t f =
187+ traceIt t (createUnencodedMessage ((Nothing :: Maybe (SendPort TraceOk)), f))
188+
189+setTraceFlagsSync :: Tracer -> SendPort TraceOk -> TraceFlags -> IO ()
190+setTraceFlagsSync t s f =
191+ traceIt t (createUnencodedMessage ((Just s), f))
192+
193+getTraceFlags :: Tracer -> SendPort TraceFlags -> IO ()
194+getTraceFlags t s = traceIt t (createUnencodedMessage s)
195+
196+getCurrentTraceClient :: Tracer -> SendPort (Maybe ProcessId) -> IO ()
197+getCurrentTraceClient t s = traceIt t (createUnencodedMessage s)
198+
199+class Traceable a where
200+ uod :: [a] -> TraceSubject
201+
202+instance Traceable ProcessId where
203+ uod = TraceProcs . Set.fromList
204+
205+instance Traceable String where
206+ uod = TraceNames . Set.fromList
207+
208+traceOnly :: Traceable a => [a] -> Maybe TraceSubject
209+traceOnly = Just . uod
210+
211+traceOn :: Maybe TraceSubject
212+traceOn = Just TraceAll
213+
214+traceOff :: Maybe TraceSubject
215+traceOff = Nothing
216+
217+traceIt :: Tracer -> Message -> IO ()
218+traceIt InactiveTracer _ = return ()
219+traceIt (ActiveTracer _ wqRef) msg = do
220+ mQueue <- deRefWeak wqRef
221+ forM_ mQueue $ \queue -> enqueue queue msg
222+
123223--------------------------------------------------------------------------------
124224-- Binary Instances --
125225--------------------------------------------------------------------------------
src/Control/Distributed/Process/Node.hs+14−4
@@ -149,13 +149,14 @@ import Control.Distributed.Process.Internal.Types
149149 , firstNonReservedProcessId
150150 , ImplicitReconnect(WithImplicitReconnect)
151151 )
152−import Control.Distributed.Process.Internal.Trace.Primitives
153− ( traceLogFmt
154− , traceEvent
152+import qualified Control.Distributed.Process.Internal.Trace.Remote as Trace
153+ ( remoteTable
155154 )
156155import Control.Distributed.Process.Internal.Trace.Types
157156 ( TraceArg(..)
158157 , TraceEvent(..)
158+ , traceEvent
159+ , traceLogFmt
159160 )
160161import Control.Distributed.Process.Internal.Trace.Tracer
161162 ( traceController
@@ -190,7 +191,7 @@ import Unsafe.Coerce
190191--------------------------------------------------------------------------------
191192
192193initRemoteTable :: RemoteTable
193−initRemoteTable = BuiltIn.remoteTable Static.initRemoteTable
194+initRemoteTable = Trace.remoteTable $ BuiltIn.remoteTable Static.initRemoteTable
194195
195196-- | Initialize a new local node.
196197newLocalNode :: NT.Transport -> RemoteTable -> IO LocalNode
@@ -241,6 +242,7 @@ createBareLocalNode endPoint rtable = do
241242
242243startTracing :: LocalNode -> IO LocalNode
243244startTracing node =
245+ -- TODO: use tryGetEnv once we drop support for GHC 7.2.x
244246 Exception.catch
245247 (getEnv "DISTRIBUTED_PROCESS_TRACE_ENABLED" >> procTracer node)
246248 (\(_ :: IOError) -> return node)
@@ -259,9 +261,17 @@ startDefaultTracer node =
259261 (\(_ :: IOError) -> return Nothing)
260262 where go :: LocalNode -> IO (Maybe ProcessId)
261263 go node' = do
264+ registerTraceController node'
262265 pid <- forkProcess node' defaultTracer
263266 return (Just pid)
264267
268+ registerTraceController :: LocalNode -> IO ()
269+ registerTraceController n =
270+ let t = localTracer n in
271+ case t of
272+ InactiveTracer -> return ()
273+ (ActiveTracer pid _) -> runProcess n $ register "trace.controller" pid
274+
265275-- | Start and register the service processes on a node
266276startServiceProcesses :: LocalNode -> IO ()
267277startServiceProcesses node = do
tests/TestTracing.hs+63
@@ -2,6 +2,7 @@
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33module Main where
44
5+import Control.Concurrent (threadDelay)
56import Control.Concurrent.MVar
67 ( MVar
78 , newEmptyMVar
@@ -259,6 +260,64 @@ testTraceLayering result = do
259260 })
260261 liftIO $ putMVar result' ()
261262
263+testRemoteTraceRelay :: TestResult Bool -> Process ()
264+testRemoteTraceRelay result =
265+ let flags = defaultTraceFlags { traceSpawned = traceOn }
266+ in do
267+ node2 <- liftIO $ mkNode "8082"
268+ mvNid <- liftIO $ newEmptyMVar
269+
270+ -- As well as needing node2's NodeId, we want to
271+ -- redirect all its logs back here, to avoid generating
272+ -- garbage on stderr for the duration of the test run.
273+ -- Here we set up that relay, and then wait for a signal
274+ -- that the tracer (on node1) has seen the expected
275+ -- TraceEvSpawned message, at which point we're finished
276+ (Just log) <- whereis "logger"
277+ pid <- liftIO $ forkProcess node2 $ do
278+ logRelay <- spawnLocal $ relay log
279+ reregister "logger" logRelay
280+ getSelfNode >>= stash mvNid >> (expect :: Process ())
281+
282+ nid <- liftIO $ takeMVar mvNid
283+ mref <- monitor pid
284+ observedPid <- liftIO $ newEmptyMVar
285+ spawnedPid <- liftIO $ newEmptyMVar
286+ setTraceFlagsRemote flags nid
287+
288+ withFlags defaultTraceFlags { traceSpawned = traceOn } $ do
289+ withTracer
290+ (\ev ->
291+ case ev of
292+ TraceEvSpawned p -> stash observedPid p >> send pid ()
293+ _ -> return ()) $ do
294+ relayPid <- startTraceRelay nid
295+ liftIO $ threadDelay 1000000
296+ p <- liftIO $ forkProcess node2 $ do
297+ expectTimeout 1000000 :: Process (Maybe ())
298+ return ()
299+ stash spawnedPid p
300+
301+ -- Now we wait for (the outer) pid to exit. This won't happen until
302+ -- our tracer has seen the trace event for `p' and send `p' the
303+ -- message its waiting for prior to exiting
304+ receiveWait [
305+ matchIf (\(ProcessMonitorNotification mref' _ _) -> mref == mref')
306+ (\_ -> return ())
307+ ]
308+
309+ relayRef <- monitor relayPid
310+ kill relayPid "stop"
311+ receiveWait [
312+ matchIf (\(ProcessMonitorNotification rref' _ _) -> rref' == relayRef)
313+ (\_ -> return ())
314+ ]
315+ observed <- liftIO $ takeMVar observedPid
316+ expected <- liftIO $ takeMVar spawnedPid
317+ stash result (observed == expected)
318+ -- and just to be polite...
319+ liftIO $ closeLocalNode node2
320+
262321tests :: LocalNode -> IO [Test]
263322tests node1 = do
264323 -- if we execute the test cases in parallel, the
@@ -298,6 +357,10 @@ tests node1 = do
298357 (delayedAssertion
299358 "expected blah"
300359 node1 () testTraceLayering lock)
360+ , testCase "Remote Trace Relay"
361+ (delayedAssertion
362+ "expected blah"
363+ node1 True testRemoteTraceRelay lock)
301364 ] ]
302365 False ->
303366 return []