@@ -40,7 +40,7 @@ import qualified Data.Set as Set
42import Data.Foldable (forM_)
42import Data.Foldable (forM_)
43−import Data.Maybe (isJust, isNothing, catMaybes)
43+import Data.Maybe (isJust, fromJust, isNothing, catMaybes)
44import Data.Typeable (Typeable)
44import Data.Typeable (Typeable)
45import Control.Category ((>>>))
45import Control.Category ((>>>))
46import Control.Applicative ((<$>))
46import Control.Applicative ((<$>))
@@ -68,6 +68,7 @@ import Control.Distributed.Process.Internal.StrictMVar
68 , takeMVar
68 , takeMVar
70import Control.Concurrent.Chan (newChan, writeChan, readChan)
70import Control.Concurrent.Chan (newChan, writeChan, readChan)
71+import qualified Control.Concurrent.MVar as MVar (newEmptyMVar, takeMVar)
71import Control.Concurrent.STM
72import Control.Concurrent.STM
72 ( atomically
73 ( atomically
@@ -105,7 +106,7 @@ import Control.Distributed.Process.Internal.Types
105 , LocalProcessId(..)
106 , LocalProcessId(..)
106 , ProcessId(..)
107 , ProcessId(..)
107 , LocalNode(..)
108 , LocalNode(..)
108− , Tracer(InactiveTracer)
109+ , Tracer(..)
109 , LocalNodeState(..)
110 , LocalNodeState(..)
110 , LocalProcess(..)
111 , LocalProcess(..)
111 , LocalProcessState(..)
112 , LocalProcessState(..)
@@ -148,11 +149,19 @@ import Control.Distributed.Process.Internal.Types
148 , firstNonReservedProcessId
149 , firstNonReservedProcessId
149 , ImplicitReconnect(WithImplicitReconnect)
150 , ImplicitReconnect(WithImplicitReconnect)
152+import qualified Control.Distributed.Process.Internal.Trace.Remote as Trace
151−import Control.Distributed.Process.Internal.Trace
155+import Control.Distributed.Process.Internal.Trace.Types
152 ( TraceArg(..)
156 ( TraceArg(..)
153− , traceFormat
157+ , TraceEvent(..)
154− , startTracing
158+ , traceEvent
155− , stopTracer
159+ , traceLogFmt
162+import Control.Distributed.Process.Internal.Trace.Tracer
157import Control.Distributed.Process.Serializable (Serializable)
166import Control.Distributed.Process.Serializable (Serializable)
158import Control.Distributed.Process.Internal.Messaging
167import Control.Distributed.Process.Internal.Messaging
@@ -175,7 +184,7 @@ import qualified Control.Distributed.Process.Internal.StrictContainerAccessors a
175 ( mapMaybe
184 ( mapMaybe
176 , mapDefault
185 , mapDefault
178−
187+import System.Environment (getEnv)
179import Unsafe.Coerce
188import Unsafe.Coerce
181--------------------------------------------------------------------------------
190--------------------------------------------------------------------------------
@@ -183,7 +192,7 @@ import Unsafe.Coerce
183--------------------------------------------------------------------------------
192--------------------------------------------------------------------------------
185initRemoteTable :: RemoteTable
194initRemoteTable :: RemoteTable
186−initRemoteTable = BuiltIn.remoteTable Static.initRemoteTable
195+initRemoteTable = Trace.remoteTable $ BuiltIn.remoteTable Static.initRemoteTable
188-- | Initialize a new local node.
197-- | Initialize a new local node.
189newLocalNode :: NT.Transport -> RemoteTable -> IO LocalNode
198newLocalNode :: NT.Transport -> RemoteTable -> IO LocalNode
@@ -232,10 +241,51 @@ createBareLocalNode endPoint rtable = do
232 , ctrlMsgSignal = SigShutdown
241 , ctrlMsgSignal = SigShutdown
244+startTracing :: LocalNode -> IO LocalNode
246+ -- TODO: use tryGetEnv once we drop support for GHC 7.2.x
248+ (getEnv "DISTRIBUTED_PROCESS_TRACE_ENABLED" >> procTracer node)
249+ (\(_ :: IOError) -> return node)
250+ where procTracer :: LocalNode -> IO LocalNode
252+ -- see note [tracer/forkProcess races]
253+ mv <- MVar.newEmptyMVar
254+ pid <- forkProcess n (traceController mv)
255+ wqRef <- MVar.takeMVar mv
256+ return n { localTracer = (ActiveTracer pid wqRef) }
258+startDefaultTracer :: LocalNode -> IO (Maybe ProcessId)
259+startDefaultTracer node =
261+ (getEnv "DISTRIBUTED_PROCESS_TRACE_ENABLED" >> go node)
262+ (\(_ :: IOError) -> return Nothing)
263+ where go :: LocalNode -> IO (Maybe ProcessId)
265+ registerTraceController node'
266+ pid <- forkProcess node' defaultTracer
267+ enableTrace (localTracer node') pid
270+ registerTraceController :: LocalNode -> IO ()
271+ registerTraceController n =
272+ let t = localTracer n in
274+ InactiveTracer -> return ()
275+ (ActiveTracer pid _) -> runProcess n $ register "trace.controller" pid
235-- | Start and register the service processes on a node
277-- | Start and register the service processes on a node
236−-- (for now, this is only the logger)
237startServiceProcesses :: LocalNode -> IO ()
278startServiceProcesses :: LocalNode -> IO ()
238startServiceProcesses node = do
279startServiceProcesses node = do
280+ -- tracing /spawns/ relies on the tracer being enabled, but we start
281+ -- the default tracer first, even though it might @nsend@ to the logger
282+ -- before /that/ process has started - this is a totally harmless race
283+ -- however, so we deliberably ignore it
284+ mPid <- startDefaultTracer node
286+ Nothing -> return ()
287+ Just pid -> runProcess node $ register "tracer.initial" pid
239 logger <- forkProcess node loop
289 logger <- forkProcess node loop
240 runProcess node $ register "logger" logger
290 runProcess node $ register "logger" logger
@@ -257,6 +307,7 @@ startServiceProcesses node = do
257-- TODO: for now we just close the associated endpoint
307-- TODO: for now we just close the associated endpoint
258closeLocalNode :: LocalNode -> IO ()
308closeLocalNode :: LocalNode -> IO ()
259closeLocalNode node =
309closeLocalNode node =
310+ -- TODO: close all our processes, surely!?
260 NT.closeEndPoint (localEndPoint node)
311 NT.closeEndPoint (localEndPoint node)
262-- | Run a process on a local node and wait for it to finish
313-- | Run a process on a local node and wait for it to finish
@@ -306,6 +357,10 @@ forkProcess node proc = modifyMVar (localState node) startProcess
307 return (tid', lproc)
358 return (tid', lproc)
361+ -- see note [tracer/forkProcess races]
362+ trace node (TraceEvSpawned pid)
309 if lpidCounter lpid == maxBound
364 if lpidCounter lpid == maxBound
311 newUnique <- randomIO
366 newUnique <- randomIO
@@ -331,6 +386,15 @@ forkProcess node proc = modifyMVar (localState node) startProcess
331 . (localConnections ^= unaffected)
386 . (localConnections ^= unaffected)
389+-- note [tracer/forkProcess races]
391+-- Our startTracing function uses forkProcess to start the trace controller
392+-- process, and of course forkProcess attempts to call traceEvent once the
393+-- process has started. This is harmless, as the localTracer is not updated
394+-- until /after/ the initial forkProcess completes, so the first call to
395+-- traceEvent behaves as if tracing were disabled (i.e., it is ignored).
334--------------------------------------------------------------------------------
398--------------------------------------------------------------------------------
335-- Handle incoming messages --
399-- Handle incoming messages --
336--------------------------------------------------------------------------------
400--------------------------------------------------------------------------------
@@ -339,7 +403,7 @@ type IncomingConnection = (NT.EndPointAddress, IncomingTarget)
340data IncomingTarget =
404data IncomingTarget =
342− | ToProc (Weak (CQueue Message))
406+ | ToProc ProcessId (Weak (CQueue Message))
343 | ToChan TypedChannel
407 | ToChan TypedChannel
@@ -374,20 +438,24 @@ handleIncomingMessages node = go initConnectionState
374 case event of
438 case event of
375 NT.ConnectionOpened cid rel theirAddr ->
439 NT.ConnectionOpened cid rel theirAddr ->
376 if rel == NT.ReliableOrdered
440 if rel == NT.ReliableOrdered
442+ trace node (TraceEvConnected cid)
377− then go ( (incomingAt cid ^= Just (theirAddr, Uninit))
444+ (incomingAt cid ^= Just (theirAddr, Uninit))
378 . (incomingFrom theirAddr ^: Set.insert cid)
445 . (incomingFrom theirAddr ^: Set.insert cid)
381 else invalidRequest cid st
448 else invalidRequest cid st
382 NT.Received cid payload ->
449 NT.Received cid payload ->
383 case st ^. incomingAt cid of
450 case st ^. incomingAt cid of
384− Just (_, ToProc weakQueue) -> do
451+ Just (_, ToProc pid weakQueue) -> do
385 mQueue <- deRefWeak weakQueue
452 mQueue <- deRefWeak weakQueue
386 forM_ mQueue $ \queue -> do
453 forM_ mQueue $ \queue -> do
387 -- TODO: if we find that the queue is Nothing, should we remove
454 -- TODO: if we find that the queue is Nothing, should we remove
388 -- it from the NC state? (and same for channels, below)
455 -- it from the NC state? (and same for channels, below)
389 let msg = payloadToMessage payload
456 let msg = payloadToMessage payload
390 enqueue queue msg -- 'enqueue' is strict
457 enqueue queue msg -- 'enqueue' is strict
458+ trace node (TraceEvReceived pid msg)
392 Just (_, ToChan (TypedChannel chan')) -> do
460 Just (_, ToChan (TypedChannel chan')) -> do
393 mChan <- deRefWeak chan'
461 mChan <- deRefWeak chan'
@@ -408,7 +476,7 @@ handleIncomingMessages node = go initConnectionState
408 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
476 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
409 case mProc of
477 case mProc of
410 Just proc ->
478 Just proc ->
411− go (incomingAt cid ^= Just (src, ToProc (processWeakQ proc)) $ st)
479+ go (incomingAt cid ^= Just (src, ToProc pid (processWeakQ proc)) $ st)
412 Nothing ->
480 Nothing ->
413 invalidRequest cid st
481 invalidRequest cid st
414 SendPortIdentifier chId -> do
482 SendPortIdentifier chId -> do
@@ -435,7 +503,8 @@ handleIncomingMessages node = go initConnectionState
435 case st ^. incomingAt cid of
503 case st ^. incomingAt cid of
436 Nothing ->
504 Nothing ->
437 invalidRequest cid st
505 invalidRequest cid st
438− Just (src, _) ->
506+ Just (src, _) -> do
507+ trace node (TraceEvDisconnected cid)
439 go ( (incomingAt cid ^= Nothing)
508 go ( (incomingAt cid ^= Nothing)
440 . (incomingFrom src ^: Set.delete cid)
509 . (incomingFrom src ^: Set.delete cid)
@@ -454,18 +523,15 @@ handleIncomingMessages node = go initConnectionState
456 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
525 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
457− fatal $ "Cloud Haskell fatal error: end point failed: " ++ str
526+ fail $ "Cloud Haskell fatal error: end point failed: " ++ str
458 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
527 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
459− fatal $ "Cloud Haskell fatal error: transport failed: " ++ str
528+ fail $ "Cloud Haskell fatal error: transport failed: " ++ str
460 NT.EndPointClosed ->
529 NT.EndPointClosed ->
461− stopTracer (localTracer node) >> return ()
530+ return ()
462 NT.ReceivedMulticast _ _ ->
531 NT.ReceivedMulticast _ _ ->
463 -- If we received a multicast message, something went horribly wrong
532 -- If we received a multicast message, something went horribly wrong
464 -- and we just give up
533 -- and we just give up
465− fatal "Cloud Haskell fatal error: received unexpected multicast"
534+ fail "Cloud Haskell fatal error: received unexpected multicast"
467− fatal :: String -> IO ()
468− fatal msg = stopTracer (localTracer node) >> fail msg
470 invalidRequest :: NT.ConnectionId -> ConnectionState -> IO ()
536 invalidRequest :: NT.ConnectionId -> ConnectionState -> IO ()
471 invalidRequest cid st = do
537 invalidRequest cid st = do
@@ -473,7 +539,7 @@ handleIncomingMessages node = go initConnectionState
473 -- node. That is, we should report the remote node as having died, and we
539 -- node. That is, we should report the remote node as having died, and we
474 -- should close incoming connections (this requires a Transport layer
540 -- should close incoming connections (this requires a Transport layer
475 -- extension).
541 -- extension).
476− traceEventFmtIO node "" [(TraceStr "[network] invalid request: "),
542+ traceEventFmtIO node "" [(TraceStr " [network] invalid request: "),
477 (Trace cid)]
543 (Trace cid)]
478 go ( incomingAt cid ^= Nothing
544 go ( incomingAt cid ^= Nothing
@@ -534,23 +600,22 @@ instance Show ProcessKillException where
535traceNotifyDied :: LocalNode -> Identifier -> DiedReason -> NC ()
601traceNotifyDied :: LocalNode -> Identifier -> DiedReason -> NC ()
536traceNotifyDied node ident reason =
602traceNotifyDied node ident reason =
603+ -- TODO: sendPortDied notifications
604+ liftIO $ withLocalTracer node $ \t ->
537− case reason of
605+ case ident of
606+ (NodeIdentifier nid) -> traceEvent t (TraceEvNodeDied nid reason)
607+ (ProcessIdentifier pid) -> traceEvent t (TraceEvDied pid reason)
538− DiedNormal -> return ()
608+ _ -> return ()
539− _ -> traceNcEventFmt node " "
540− [(TraceStr "[node-controller]"),
544−traceNcEventFmt :: LocalNode -> String -> [TraceArg] -> NC ()
545−traceNcEventFmt node fmt args =
546− liftIO $ traceEventFmtIO node fmt args
548traceEventFmtIO :: LocalNode
610traceEventFmtIO :: LocalNode
549 -> String
611 -> String
550 -> [TraceArg]
612 -> [TraceArg]
552traceEventFmtIO node fmt args =
614traceEventFmtIO node fmt args =
553− withLocalTracer node $ \t -> traceFormat t fmt args
615+ withLocalTracer node $ \t -> traceLogFmt t fmt args
617+trace :: LocalNode -> TraceEvent -> IO ()
618+trace node ev = withLocalTracer node $ \t -> traceEvent t ev
555withLocalTracer :: LocalNode -> (Tracer -> IO ()) -> IO ()
620withLocalTracer :: LocalNode -> (Tracer -> IO ()) -> IO ()
556withLocalTracer node act = act (localTracer node)
621withLocalTracer node act = act (localTracer node)
@@ -597,7 +662,7 @@ nodeController = do
597 NCMsg _ (NamedSend label msg') ->
662 NCMsg _ (NamedSend label msg') ->
598 ncEffectNamedSend label msg'
663 ncEffectNamedSend label msg'
599 NCMsg _ (LocalSend to msg') ->
664 NCMsg _ (LocalSend to msg') ->
600− ncEffectLocalSend to msg'
665+ ncEffectLocalSend node to msg'
601 NCMsg _ (LocalPortSend to msg') ->
666 NCMsg _ (LocalPortSend to msg') ->
602 ncEffectLocalPortSend to msg'
667 ncEffectLocalPortSend to msg'
603 NCMsg (ProcessIdentifier from) (Kill to reason) ->
668 NCMsg (ProcessIdentifier from) (Kill to reason) ->
@@ -720,6 +785,7 @@ ncEffectSpawn pid cProc ref = do
720 mProc <- unClosure cProc
785 mProc <- unClosure cProc
721 -- If the closure does not exist, we spawn a process that throws an exception
786 -- If the closure does not exist, we spawn a process that throws an exception
722 -- This allows the remote node to find out what's happening
787 -- This allows the remote node to find out what's happening
723 let proc = case mProc of
789 let proc = case mProc of
724 Left err -> fail $ "Error: Could not resolve closure: " ++ err
790 Left err -> fail $ "Error: Could not resolve closure: " ++ err
725 Right p -> p
791 Right p -> p
@@ -750,6 +816,9 @@ ncEffectRegister from label atnode mPid reregistration = do
750 then do when (isOk) $
816 then do when (isOk) $
751 do modify' $ registeredHereFor label ^= mPid
817 do modify' $ registeredHereFor label ^= mPid
752 updateRemote node currentVal mPid
818 updateRemote node currentVal mPid
820+ (Just p) -> liftIO $ trace node (TraceEvRegistered p label)
821+ Nothing -> liftIO $ trace node (TraceEvUnRegistered (fromJust currentVal) label)
753 liftIO $ sendMessage node
822 liftIO $ sendMessage node
754 (NodeIdentifier (localNodeId node))
823 (NodeIdentifier (localNodeId node))
755 (ProcessIdentifier from)
824 (ProcessIdentifier from)
@@ -772,6 +841,7 @@ ncEffectRegister from label atnode mPid reregistration = do
772 updateRemote _ _ _ = return ()
841 updateRemote _ _ _ = return ()
773 maybeify f Nothing = unmaybeify $ f []
842 maybeify f Nothing = unmaybeify $ f []
774 maybeify f (Just x) = unmaybeify $ f x
843 maybeify f (Just x) = unmaybeify $ f x
775 unmaybeify [] = Nothing
845 unmaybeify [] = Nothing
776 unmaybeify x = Just x
846 unmaybeify x = Just x
777 incList [] tag = [(tag,1)]
847 incList [] tag = [(tag,1)]
@@ -812,16 +882,19 @@ ncEffectNamedSend label msg = do
812 forM_ mPid $ \pid -> postMessage pid msg
882 forM_ mPid $ \pid -> postMessage pid msg
814-- [Issue #DP-20]
884-- [Issue #DP-20]
815−ncEffectLocalSend :: ProcessId -> Message -> NC ()
885+ncEffectLocalSend :: LocalNode -> ProcessId -> Message -> NC ()
816−ncEffectLocalSend = postMessage
886+ncEffectLocalSend node to msg =
887+ liftIO $ withLocalProc node to $ \p -> do
888+ enqueue (processQueue p) msg
889+ trace node (TraceEvReceived to msg)
818-- [Issue #DP-20]
891-- [Issue #DP-20]
819ncEffectLocalPortSend :: SendPortId -> Message -> NC ()
892ncEffectLocalPortSend :: SendPortId -> Message -> NC ()
820−ncEffectLocalPortSend from msg =
893+ncEffectLocalPortSend from msg = do
821 let pid = sendPortProcessId from
895 let pid = sendPortProcessId from
822 cid = sendPortLocalId from
896 cid = sendPortLocalId from
824− withLocalProc pid $ \proc -> do
897+ liftIO $ withLocalProc node pid $ \proc -> do
825 mChan <- withMVar (processState proc) $ return . (^. typedChannelWithId cid)
898 mChan <- withMVar (processState proc) $ return . (^. typedChannelWithId cid)
826 case mChan of
899 case mChan of
827 -- in the unlikely event we know nothing about this channel id,
900 -- in the unlikely event we know nothing about this channel id,
@@ -995,21 +1068,21 @@ postAsMessage pid = postMessage pid . createUnencodedMessage
996postMessage :: ProcessId -> Message -> NC ()
1069postMessage :: ProcessId -> Message -> NC ()
997postMessage pid msg = do
1070postMessage pid msg = do
998− withLocalProc pid $ \p -> enqueue (processQueue p) msg
1072+ liftIO $ withLocalProc node pid $ \p -> enqueue (processQueue p) msg
1000throwException :: Exception e => ProcessId -> e -> NC ()
1074throwException :: Exception e => ProcessId -> e -> NC ()
1001−throwException pid e = withLocalProc pid $ \p ->
1075+throwException pid e = do
1002− throwTo (processThread p) e
1004−withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()
1005−withLocalProc pid p = do
1006 node <- ask
1076 node <- ask
1007− liftIO $ do
1077+ liftIO $ withLocalProc node pid $ \p -> throwTo (processThread p) e
1079+withLocalProc :: LocalNode -> ProcessId -> (LocalProcess -> IO ()) -> IO ()
1080+withLocalProc node pid p =
1008− -- By [Unified: table 6, rule missing_process] messages to dead processes
1081+ -- By [Unified: table 6, rule missing_process] messages to dead processes
1009− -- can silently be dropped
1082+ -- can silently be dropped
1010− let lpid = processLocalId pid
1083+ let lpid = processLocalId pid in do
1011− mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
1084+ mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
1012− forM_ mProc p
1085+ forM_ mProc p
1014--------------------------------------------------------------------------------
1087--------------------------------------------------------------------------------
1015-- Accessors --
1088-- Accessors --