@@ -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,17 @@ import Control.Distributed.Process.Internal.Types
148 , firstNonReservedProcessId
149 , firstNonReservedProcessId
149 , ImplicitReconnect(WithImplicitReconnect)
150 , ImplicitReconnect(WithImplicitReconnect)
151−import Control.Distributed.Process.Internal.Trace
152+import Control.Distributed.Process.Internal.Trace.Primitives
156+import Control.Distributed.Process.Internal.Trace.Types
152 ( TraceArg(..)
157 ( TraceArg(..)
153− , traceFormat
158+ , TraceEvent(..)
154− , startTracing
159+ )
155− , stopTracer
160+import Control.Distributed.Process.Internal.Trace.Tracer
157import Control.Distributed.Process.Serializable (Serializable)
164import Control.Distributed.Process.Serializable (Serializable)
158import Control.Distributed.Process.Internal.Messaging
165import Control.Distributed.Process.Internal.Messaging
@@ -175,7 +182,7 @@ import qualified Control.Distributed.Process.Internal.StrictContainerAccessors a
175 ( mapMaybe
182 ( mapMaybe
176 , mapDefault
183 , mapDefault
178−
185+import System.Environment (getEnv)
179import Unsafe.Coerce
186import Unsafe.Coerce
181--------------------------------------------------------------------------------
188--------------------------------------------------------------------------------
@@ -232,10 +239,41 @@ createBareLocalNode endPoint rtable = do
232 , ctrlMsgSignal = SigShutdown
239 , ctrlMsgSignal = SigShutdown
242+startTracing :: LocalNode -> IO LocalNode
245+ (getEnv "DISTRIBUTED_PROCESS_TRACE_ENABLED" >> procTracer node)
246+ (\(_ :: IOError) -> return node)
247+ where procTracer :: LocalNode -> IO LocalNode
249+ -- see note [tracer/forkProcess races]
250+ mv <- MVar.newEmptyMVar
251+ pid <- forkProcess n (traceController mv)
252+ wqRef <- MVar.takeMVar mv
253+ return n { localTracer = (ActiveTracer pid wqRef) }
255+startDefaultTracer :: LocalNode -> IO (Maybe ProcessId)
256+startDefaultTracer node =
258+ (getEnv "DISTRIBUTED_PROCESS_TRACE_ENABLED" >> go node)
259+ (\(_ :: IOError) -> return Nothing)
260+ where go :: LocalNode -> IO (Maybe ProcessId)
262+ pid <- forkProcess node' defaultTracer
235-- | Start and register the service processes on a node
265-- | Start and register the service processes on a node
236−-- (for now, this is only the logger)
237startServiceProcesses :: LocalNode -> IO ()
266startServiceProcesses :: LocalNode -> IO ()
238startServiceProcesses node = do
267startServiceProcesses node = do
268+ -- tracing /spawns/ relies on the tracer being enabled, but we start
269+ -- the default tracer first, even though it might @nsend@ to the logger
270+ -- before /that/ process has started - this is a totally harmless race
271+ mPid <- startDefaultTracer node
273+ Nothing -> return ()
275+ runProcess node (register "tracer" pid >> register "tracer.initial" pid)
239 logger <- forkProcess node loop
277 logger <- forkProcess node loop
240 runProcess node $ register "logger" logger
278 runProcess node $ register "logger" logger
@@ -257,6 +295,7 @@ startServiceProcesses node = do
257-- TODO: for now we just close the associated endpoint
295-- TODO: for now we just close the associated endpoint
258closeLocalNode :: LocalNode -> IO ()
296closeLocalNode :: LocalNode -> IO ()
259closeLocalNode node =
297closeLocalNode node =
298+ -- TODO: close all our processes, surely!?
260 NT.closeEndPoint (localEndPoint node)
299 NT.closeEndPoint (localEndPoint node)
262-- | Run a process on a local node and wait for it to finish
301-- | Run a process on a local node and wait for it to finish
@@ -306,6 +345,10 @@ forkProcess node proc = modifyMVar (localState node) startProcess
307 return (tid', lproc)
346 return (tid', lproc)
349+ -- see note [tracer/forkProcess races]
350+ trace node (TraceEvSpawned pid)
309 if lpidCounter lpid == maxBound
352 if lpidCounter lpid == maxBound
311 newUnique <- randomIO
354 newUnique <- randomIO
@@ -331,6 +374,15 @@ forkProcess node proc = modifyMVar (localState node) startProcess
331 . (localConnections ^= unaffected)
374 . (localConnections ^= unaffected)
377+-- note [tracer/forkProcess races]
379+-- Our startTracing function uses forkProcess to start the trace controller
380+-- process, and of course forkProcess attempts to call traceEvent once the
381+-- process has started. This is harmless, as the localTracer is not updated
382+-- until /after/ the initial forkProcess completes, so the first call to
383+-- traceEvent behaves as if tracing were disabled (i.e., it is ignored).
334--------------------------------------------------------------------------------
386--------------------------------------------------------------------------------
335-- Handle incoming messages --
387-- Handle incoming messages --
336--------------------------------------------------------------------------------
388--------------------------------------------------------------------------------
@@ -339,7 +391,7 @@ type IncomingConnection = (NT.EndPointAddress, IncomingTarget)
340data IncomingTarget =
392data IncomingTarget =
342− | ToProc (Weak (CQueue Message))
394+ | ToProc ProcessId (Weak (CQueue Message))
343 | ToChan TypedChannel
395 | ToChan TypedChannel
@@ -374,20 +426,24 @@ handleIncomingMessages node = go initConnectionState
374 case event of
426 case event of
375 NT.ConnectionOpened cid rel theirAddr ->
427 NT.ConnectionOpened cid rel theirAddr ->
376 if rel == NT.ReliableOrdered
428 if rel == NT.ReliableOrdered
430+ trace node (TraceEvConnected cid)
377− then go ( (incomingAt cid ^= Just (theirAddr, Uninit))
432+ (incomingAt cid ^= Just (theirAddr, Uninit))
378 . (incomingFrom theirAddr ^: Set.insert cid)
433 . (incomingFrom theirAddr ^: Set.insert cid)
381 else invalidRequest cid st
436 else invalidRequest cid st
382 NT.Received cid payload ->
437 NT.Received cid payload ->
383 case st ^. incomingAt cid of
438 case st ^. incomingAt cid of
384− Just (_, ToProc weakQueue) -> do
439+ Just (_, ToProc pid weakQueue) -> do
385 mQueue <- deRefWeak weakQueue
440 mQueue <- deRefWeak weakQueue
386 forM_ mQueue $ \queue -> do
441 forM_ mQueue $ \queue -> do
387 -- TODO: if we find that the queue is Nothing, should we remove
442 -- TODO: if we find that the queue is Nothing, should we remove
388 -- it from the NC state? (and same for channels, below)
443 -- it from the NC state? (and same for channels, below)
389 let msg = payloadToMessage payload
444 let msg = payloadToMessage payload
390 enqueue queue msg -- 'enqueue' is strict
445 enqueue queue msg -- 'enqueue' is strict
446+ trace node (TraceEvReceived pid msg)
392 Just (_, ToChan (TypedChannel chan')) -> do
448 Just (_, ToChan (TypedChannel chan')) -> do
393 mChan <- deRefWeak chan'
449 mChan <- deRefWeak chan'
@@ -408,7 +464,7 @@ handleIncomingMessages node = go initConnectionState
408 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
464 mProc <- withMVar state $ return . (^. localProcessWithId lpid)
409 case mProc of
465 case mProc of
410 Just proc ->
466 Just proc ->
411− go (incomingAt cid ^= Just (src, ToProc (processWeakQ proc)) $ st)
467+ go (incomingAt cid ^= Just (src, ToProc pid (processWeakQ proc)) $ st)
412 Nothing ->
468 Nothing ->
413 invalidRequest cid st
469 invalidRequest cid st
414 SendPortIdentifier chId -> do
470 SendPortIdentifier chId -> do
@@ -435,7 +491,8 @@ handleIncomingMessages node = go initConnectionState
435 case st ^. incomingAt cid of
491 case st ^. incomingAt cid of
436 Nothing ->
492 Nothing ->
437 invalidRequest cid st
493 invalidRequest cid st
438− Just (src, _) ->
494+ Just (src, _) -> do
495+ trace node (TraceEvDisconnected cid)
439 go ( (incomingAt cid ^= Nothing)
496 go ( (incomingAt cid ^= Nothing)
440 . (incomingFrom src ^: Set.delete cid)
497 . (incomingFrom src ^: Set.delete cid)
@@ -454,18 +511,15 @@ handleIncomingMessages node = go initConnectionState
456 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
513 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
457− fatal $ "Cloud Haskell fatal error: end point failed: " ++ str
514+ fail $ "Cloud Haskell fatal error: end point failed: " ++ str
458 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
515 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
459− fatal $ "Cloud Haskell fatal error: transport failed: " ++ str
516+ fail $ "Cloud Haskell fatal error: transport failed: " ++ str
460 NT.EndPointClosed ->
517 NT.EndPointClosed ->
461− stopTracer (localTracer node) >> return ()
518+ return ()
462 NT.ReceivedMulticast _ _ ->
519 NT.ReceivedMulticast _ _ ->
463 -- If we received a multicast message, something went horribly wrong
520 -- If we received a multicast message, something went horribly wrong
464 -- and we just give up
521 -- and we just give up
465− fatal "Cloud Haskell fatal error: received unexpected multicast"
522+ 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 ()
524 invalidRequest :: NT.ConnectionId -> ConnectionState -> IO ()
471 invalidRequest cid st = do
525 invalidRequest cid st = do
@@ -473,7 +527,7 @@ handleIncomingMessages node = go initConnectionState
473 -- node. That is, we should report the remote node as having died, and we
527 -- node. That is, we should report the remote node as having died, and we
474 -- should close incoming connections (this requires a Transport layer
528 -- should close incoming connections (this requires a Transport layer
475 -- extension).
529 -- extension).
476− traceEventFmtIO node "" [(TraceStr "[network] invalid request: "),
530+ traceEventFmtIO node "" [(TraceStr " [network] invalid request: "),
477 (Trace cid)]
531 (Trace cid)]
478 go ( incomingAt cid ^= Nothing
532 go ( incomingAt cid ^= Nothing
@@ -534,23 +588,22 @@ instance Show ProcessKillException where
535traceNotifyDied :: LocalNode -> Identifier -> DiedReason -> NC ()
589traceNotifyDied :: LocalNode -> Identifier -> DiedReason -> NC ()
536traceNotifyDied node ident reason =
590traceNotifyDied node ident reason =
591+ -- TODO: sendPortDied notifications
592+ liftIO $ withLocalTracer node $ \t ->
537− case reason of
593+ case ident of
594+ (NodeIdentifier nid) -> traceEvent t (TraceEvNodeDied nid reason)
595+ (ProcessIdentifier pid) -> traceEvent t (TraceEvDied pid reason)
538− DiedNormal -> return ()
596+ _ -> 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
598traceEventFmtIO :: LocalNode
549 -> String
599 -> String
550 -> [TraceArg]
600 -> [TraceArg]
552traceEventFmtIO node fmt args =
602traceEventFmtIO node fmt args =
553− withLocalTracer node $ \t -> traceFormat t fmt args
603+ withLocalTracer node $ \t -> traceLogFmt t fmt args
605+trace :: LocalNode -> TraceEvent -> IO ()
606+trace node ev = withLocalTracer node $ \t -> traceEvent t ev
555withLocalTracer :: LocalNode -> (Tracer -> IO ()) -> IO ()
608withLocalTracer :: LocalNode -> (Tracer -> IO ()) -> IO ()
556withLocalTracer node act = act (localTracer node)
609withLocalTracer node act = act (localTracer node)
@@ -597,7 +650,7 @@ nodeController = do
597 NCMsg _ (NamedSend label msg') ->
650 NCMsg _ (NamedSend label msg') ->
598 ncEffectNamedSend label msg'
651 ncEffectNamedSend label msg'
599 NCMsg _ (LocalSend to msg') ->
652 NCMsg _ (LocalSend to msg') ->
600− ncEffectLocalSend to msg'
653+ ncEffectLocalSend node to msg'
601 NCMsg _ (LocalPortSend to msg') ->
654 NCMsg _ (LocalPortSend to msg') ->
602 ncEffectLocalPortSend to msg'
655 ncEffectLocalPortSend to msg'
603 NCMsg (ProcessIdentifier from) (Kill to reason) ->
656 NCMsg (ProcessIdentifier from) (Kill to reason) ->
@@ -720,6 +773,7 @@ ncEffectSpawn pid cProc ref = do
720 mProc <- unClosure cProc
773 mProc <- unClosure cProc
721 -- If the closure does not exist, we spawn a process that throws an exception
774 -- 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
775 -- This allows the remote node to find out what's happening
723 let proc = case mProc of
777 let proc = case mProc of
724 Left err -> fail $ "Error: Could not resolve closure: " ++ err
778 Left err -> fail $ "Error: Could not resolve closure: " ++ err
725 Right p -> p
779 Right p -> p
@@ -750,6 +804,9 @@ ncEffectRegister from label atnode mPid reregistration = do
750 then do when (isOk) $
804 then do when (isOk) $
751 do modify' $ registeredHereFor label ^= mPid
805 do modify' $ registeredHereFor label ^= mPid
752 updateRemote node currentVal mPid
806 updateRemote node currentVal mPid
808+ (Just p) -> liftIO $ trace node (TraceEvRegistered p label)
809+ Nothing -> liftIO $ trace node (TraceEvUnRegistered (fromJust currentVal) label)
753 liftIO $ sendMessage node
810 liftIO $ sendMessage node
754 (NodeIdentifier (localNodeId node))
811 (NodeIdentifier (localNodeId node))
755 (ProcessIdentifier from)
812 (ProcessIdentifier from)
@@ -772,6 +829,7 @@ ncEffectRegister from label atnode mPid reregistration = do
772 updateRemote _ _ _ = return ()
829 updateRemote _ _ _ = return ()
773 maybeify f Nothing = unmaybeify $ f []
830 maybeify f Nothing = unmaybeify $ f []
774 maybeify f (Just x) = unmaybeify $ f x
831 maybeify f (Just x) = unmaybeify $ f x
775 unmaybeify [] = Nothing
833 unmaybeify [] = Nothing
776 unmaybeify x = Just x
834 unmaybeify x = Just x
777 incList [] tag = [(tag,1)]
835 incList [] tag = [(tag,1)]
@@ -812,16 +870,19 @@ ncEffectNamedSend label msg = do
812 forM_ mPid $ \pid -> postMessage pid msg
870 forM_ mPid $ \pid -> postMessage pid msg
814-- [Issue #DP-20]
872-- [Issue #DP-20]
815−ncEffectLocalSend :: ProcessId -> Message -> NC ()
873+ncEffectLocalSend :: LocalNode -> ProcessId -> Message -> NC ()
816−ncEffectLocalSend = postMessage
874+ncEffectLocalSend node to msg =
875+ liftIO $ withLocalProc node to $ \p -> do
876+ enqueue (processQueue p) msg
877+ trace node (TraceEvReceived to msg)
818-- [Issue #DP-20]
879-- [Issue #DP-20]
819ncEffectLocalPortSend :: SendPortId -> Message -> NC ()
880ncEffectLocalPortSend :: SendPortId -> Message -> NC ()
820−ncEffectLocalPortSend from msg =
881+ncEffectLocalPortSend from msg = do
821 let pid = sendPortProcessId from
883 let pid = sendPortProcessId from
822 cid = sendPortLocalId from
884 cid = sendPortLocalId from
824− withLocalProc pid $ \proc -> do
885+ liftIO $ withLocalProc node pid $ \proc -> do
825 mChan <- withMVar (processState proc) $ return . (^. typedChannelWithId cid)
886 mChan <- withMVar (processState proc) $ return . (^. typedChannelWithId cid)
826 case mChan of
887 case mChan of
827 -- in the unlikely event we know nothing about this channel id,
888 -- in the unlikely event we know nothing about this channel id,
@@ -995,21 +1056,21 @@ postAsMessage pid = postMessage pid . createUnencodedMessage
996postMessage :: ProcessId -> Message -> NC ()
1057postMessage :: ProcessId -> Message -> NC ()
997postMessage pid msg = do
1058postMessage pid msg = do
998− withLocalProc pid $ \p -> enqueue (processQueue p) msg
1060+ liftIO $ withLocalProc node pid $ \p -> enqueue (processQueue p) msg
1000throwException :: Exception e => ProcessId -> e -> NC ()
1062throwException :: Exception e => ProcessId -> e -> NC ()
1001−throwException pid e = withLocalProc pid $ \p ->
1063+throwException pid e = do
1002− throwTo (processThread p) e
1004−withLocalProc :: ProcessId -> (LocalProcess -> IO ()) -> NC ()
1005−withLocalProc pid p = do
1006 node <- ask
1064 node <- ask
1007− liftIO $ do
1065+ liftIO $ withLocalProc node pid $ \p -> throwTo (processThread p) e
1067+withLocalProc :: LocalNode -> ProcessId -> (LocalProcess -> IO ()) -> IO ()
1068+withLocalProc node pid p =
1008− -- By [Unified: table 6, rule missing_process] messages to dead processes
1069+ -- By [Unified: table 6, rule missing_process] messages to dead processes
1009− -- can silently be dropped
1070+ -- can silently be dropped
1010− let lpid = processLocalId pid
1071+ let lpid = processLocalId pid in do
1011− mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
1072+ mProc <- withMVar (localState node) $ return . (^. localProcessWithId lpid)
1012− forM_ mProc p
1073+ forM_ mProc p
1014--------------------------------------------------------------------------------
1075--------------------------------------------------------------------------------
1015-- Accessors --
1076-- Accessors --