@@ -62,7 +62,9 @@ import Control.Distributed.Process.Internal.StrictMVar
6262 , takeMVar
6363 )
6464import Control.Concurrent.Chan (newChan, writeChan, readChan)
65−import Control.Concurrent.STM (atomically)
65+import Control.Concurrent.STM
66+ ( atomically
67+ )
6668import Control.Distributed.Process.Internal.CQueue
6769 ( CQueue
6870 , enqueue
@@ -97,6 +99,7 @@ import Control.Distributed.Process.Internal.Types
9799 , LocalProcessId(..)
98100 , ProcessId(..)
99101 , LocalNode(..)
102+ , Tracer(InactiveTracer)
100103 , LocalNodeState(..)
101104 , LocalProcess(..)
102105 , LocalProcessState(..)
@@ -108,6 +111,7 @@ import Control.Distributed.Process.Internal.Types
108111 , localPidUnique
109112 , localProcessWithId
110113 , localConnections
114+ , forever'
111115 , MonitorRef(..)
112116 , ProcessMonitorNotification(..)
113117 , NodeMonitorNotification(..)
@@ -139,6 +143,12 @@ import Control.Distributed.Process.Internal.Types
139143 , firstNonReservedProcessId
140144 , ImplicitReconnect(WithImplicitReconnect, NoImplicitReconnect)
141145 )
146+import Control.Distributed.Process.Internal.Trace
147+ ( TraceArg(..)
148+ , traceFormat
149+ , startTracing
150+ , stopTracer
151+ )
142152import Control.Distributed.Process.Serializable (Serializable)
143153import Control.Distributed.Process.Internal.Messaging
144154 ( sendBinary
@@ -195,16 +205,13 @@ createBareLocalNode endPoint rtable = do
195205 , localEndPoint = endPoint
196206 , localState = state
197207 , localCtrlChan = ctrlChan
208+ , localTracer = InactiveTracer
198209 , remoteTable = rtable
199210 }
200− void . forkIO $ runNodeController node
201− void . forkIO $ handleIncomingMessages node
202− return node
203−
204−-- Like 'Control.Monad.forever' but sans space leak
205−{-# INLINE forever' #-}
206−forever' :: Monad m => m a -> m b
207−forever' a = let a' = a >> a' in a'
211+ tracedNode <- startTracing node
212+ void . forkIO $ runNodeController tracedNode
213+ void . forkIO $ handleIncomingMessages tracedNode
214+ return tracedNode
208215
209216-- | Start and register the service processes on a node
210217-- (for now, this is only the logger)
@@ -218,6 +225,10 @@ startServiceProcesses node = do
218225 [ match $ \((time, pid, string) ::(String, ProcessId, String)) -> do
219226 liftIO . hPutStrLn stderr $ time ++ " " ++ show pid ++ ": " ++ string
220227 loop
228+ , match $ \((time, string) :: (String, String)) -> do
229+ -- this is a 'trace' message from the local node tracer
230+ liftIO . hPutStrLn stderr $ time ++ " [trace] " ++ string
231+ loop
221232 , match $ \(ch :: SendPort ()) -> -- a shutdown request
222233 sendChan ch ()
223234 ]
@@ -267,6 +278,7 @@ forkProcess node proc = modifyMVar (localState node) startProcess
267278 reason <- Exception.catch
268279 (runLocalProcess lproc proc >> return DiedNormal)
269280 (return . DiedException . (show :: SomeException -> String))
281+
270282 -- [Unified: Table 4, rules termination and exiting]
271283 modifyMVar_ (localState node) (cleanupProcess pid)
272284 writeChan (localCtrlChan node) NCMsg
@@ -423,22 +435,27 @@ handleIncomingMessages node = go initConnectionState
423435 $ st
424436 )
425437 NT.ErrorEvent (NT.TransportError NT.EventEndPointFailed str) ->
426− fail $ "Cloud Haskell fatal error: end point failed: " ++ str
438+ fatal $ "Cloud Haskell fatal error: end point failed: " ++ str
427439 NT.ErrorEvent (NT.TransportError NT.EventTransportFailed str) ->
428− fail $ "Cloud Haskell fatal error: transport failed: " ++ str
440+ fatal $ "Cloud Haskell fatal error: transport failed: " ++ str
429441 NT.EndPointClosed ->
430− return ()
442+ stopTracer (localTracer node) >> return ()
431443 NT.ReceivedMulticast _ _ ->
432444 -- If we received a multicast message, something went horribly wrong
433445 -- and we just give up
434− fail "Cloud Haskell fatal error: received unexpected multicast"
446+ fatal "Cloud Haskell fatal error: received unexpected multicast"
447+
448+ fatal :: String -> IO ()
449+ fatal msg = stopTracer (localTracer node) >> fail msg
435450
436451 invalidRequest :: NT.ConnectionId -> ConnectionState -> IO ()
437− invalidRequest cid st =
452+ invalidRequest cid st = do
438453 -- TODO: We should treat this as a fatal error on the part of the remote
439454 -- node. That is, we should report the remote node as having died, and we
440455 -- should close incoming connections (this requires a Transport layer
441456 -- extension).
457+ traceEventFmtIO node "" [(TraceStr "[network] invalid request: "),
458+ (Trace cid)]
442459 go ( incomingAt cid ^= Nothing
443460 $ st
444461 )
@@ -490,6 +507,35 @@ instance Show ProcessKillException where
490507 show (ProcessKillException pid reason) =
491508 "killed-by=" ++ show pid ++ ",reason=" ++ reason
492509
510+--------------------------------------------------------------------------------
511+-- Tracing/Debugging --
512+--------------------------------------------------------------------------------
513+
514+-- [Issue #104]
515+
516+traceNotifyDied :: LocalNode -> Identifier -> DiedReason -> NC ()
517+traceNotifyDied node ident reason =
518+ case reason of
519+ DiedNormal -> return ()
520+ _ -> traceNcEventFmt node " "
521+ [(TraceStr "[node-controller]"),
522+ (Trace ident),
523+ (Trace reason)]
524+
525+traceNcEventFmt :: LocalNode -> String -> [TraceArg] -> NC ()
526+traceNcEventFmt node fmt args =
527+ liftIO $ traceEventFmtIO node fmt args
528+
529+traceEventFmtIO :: LocalNode
530+ -> String
531+ -> [TraceArg]
532+ -> IO ()
533+traceEventFmtIO node fmt args =
534+ withLocalTracer node $ \t -> traceFormat t fmt args
535+
536+withLocalTracer :: LocalNode -> (Tracer -> IO ()) -> IO ()
537+withLocalTracer node act = act (localTracer node)
538+
493539--------------------------------------------------------------------------------
494540-- Core functionality --
495541--------------------------------------------------------------------------------
@@ -597,6 +643,7 @@ ncEffectUnmonitor from ref = do
597643ncEffectDied :: Identifier -> DiedReason -> NC ()
598644ncEffectDied ident reason = do
599645 node <- ask
646+ traceNotifyDied node ident reason
600647 (affectedLinks, unaffectedLinks) <- gets (splitNotif ident . (^. links))
601648 (affectedMons, unaffectedMons) <- gets (splitNotif ident . (^. monitors))
602649
@@ -639,7 +686,6 @@ ncEffectDied ident reason = do
639686 , ctrlMsgSignal = Died ident reason
640687 }
641688
642−
643689-- [Unified: Table 13]
644690ncEffectSpawn :: ProcessId -> Closure (Process ()) -> SpawnRef -> NC ()
645691ncEffectSpawn pid cProc ref = do
@@ -841,18 +887,18 @@ notifyDied dest src reason mRef = do
841887
842888-- | [Unified: Table 8]
843889destNid :: ProcessSignal -> Maybe NodeId
844−destNid (Link ident) = Just $ nodeOf ident
845−destNid (Unlink ident) = Just $ nodeOf ident
846−destNid (Monitor ref) = Just $ nodeOf (monitorRefIdent ref)
847−destNid (Unmonitor ref) = Just $ nodeOf (monitorRefIdent ref)
848−destNid (Spawn _ _) = Nothing
849−destNid (Register _ _ _ _) = Nothing
850−destNid (WhereIs _) = Nothing
851−destNid (NamedSend _ _) = Nothing
890+destNid (Link ident) = Just $ nodeOf ident
891+destNid (Unlink ident) = Just $ nodeOf ident
892+destNid (Monitor ref) = Just $ nodeOf (monitorRefIdent ref)
893+destNid (Unmonitor ref) = Just $ nodeOf (monitorRefIdent ref)
894+destNid (Spawn _ _) = Nothing
895+destNid (Register _ _ _ _) = Nothing
896+destNid (WhereIs _) = Nothing
897+destNid (NamedSend _ _) = Nothing
852898-- We don't need to forward 'Died' signals; if monitoring/linking is setup,
853899-- then when a local process dies the monitoring/linking machinery will take
854900-- care of notifying remote nodes
855−destNid (Died _ _) = Nothing
901+destNid (Died _ _) = Nothing
856902destNid (Kill pid _) = Just $ processNodeId pid
857903destNid (Exit pid _) = Just $ processNodeId pid
858904destNid (GetInfo pid) = Just $ processNodeId pid