1-----------------------------------------------------------------------------
3-- Module : Control.Distributed.Process.Debug
4-- Copyright : (c) Well-Typed / Tim Watson
5-- License : BSD3 (see the file LICENSE)
7-- Maintainer : Tim Watson <watson.timothy@gmail.com>
8-- Stability : experimental
9-- Portability : non-portable (requires concurrency)
11-- [Tracing/Debugging Facilities]
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.
20-- Throughout the lifecycle of a local node, the distributed-process runtime
21-- generates /trace events/, describing internal runtime activities such as
22-- the spawning and death of processes, message sending, delivery and so on.
23-- See the 'MxEvent' type's documentation for a list of all the published
24-- event types, which correspond directly to the types of 'Management' event.
25-- Users can additionally publish custom trace events in the form of
26-- 'MxLog' log messages or pass custom (i.e., completely user defined)
27-- event data using the 'traceMessage' function.
29-- All published traces are forwarded to a /tracer process/, which can be
30-- specified (and changed) at runtime using 'traceEnable'. Some pre-defined
31-- tracer processes are provided for conveniently printing to stderr, a log file
32-- or the GHC eventlog.
34-- If a tracer process crashes, no attempt is made to restart it.
36-- [Working with multiple tracer processes]
38-- The tracing facility only ever writes to a single tracer process. This
39-- invariant insulates the tracer controller and ensures a fast path for
40-- handling all trace events. /This/ module provides facilities for layering
41-- trace handlers using Cloud Haskell's built-in delegation primitives.
43-- The 'startTracer' function wraps the registered @tracer@ process with the
44-- supplied handler and also forwards trace events to the original tracer.
45-- The corresponding 'stopTracer' function terminates tracer processes in
46-- reverse of the order in which they were started, and re-registers the
47-- previous tracer process.
51-- The built in tracers provide a simple /logging/ facility that writes trace
52-- events out to either a log file, @stderr@ or the GHC eventlog. These tracers
53-- can be configured using environment variables, or specified manually using
54-- the 'traceEnable' function.
56-- When a new local node is started, the contents of several environment
57-- variables are checked to determine which default tracer process is selected.
58-- If none of these variables is set, a no-op tracer process is installed,
59-- which effectively ignores all trace messages. Note that in this case,
60-- trace events are still generated and passed through the system.
61-- Only one default tracer will be chosen - the first that contains a (valid)
62-- value. These environment variables, in the order they're examined, are:
64-- 1. @DISTRIBUTED_PROCESS_TRACE_FILE@
65-- This is checked for a valid file path. If it exists and the file can be
66-- opened for writing, all trace output will be directed thence. If the supplied
67-- path is invalid, or the file is unavailable for writing, this tracer will not
70-- 2. @DISTRIBUTED_PROCESS_TRACE_CONSOLE@
71-- This is checked for /any/ non-empty value. If set, then all trace output will
72-- be directed to the system logger process.
74-- 3. @DISTRIBUTED_PROCESS_TRACE_EVENTLOG@
75-- This is checked for /any/ non-empty value. If set, all internal traces are
76-- written to the GHC eventlog.
78-- Users of the /simplelocalnet/ Cloud Haskell backend should also note that
79-- because the trace file option only supports trace output from a single node
80-- (so as to avoid interleaving), a file trace configured for the master node
81-- will prevent slaves from tracing to the file. They will need to fall back to
82-- the console or eventlog tracers instead, which can be accomplished by setting
83-- one of these environment variables /as well/, since the latter will only be
84-- selected on slaves (when the file tracer selection fails).
86-- Support for writing to the eventlog requires specific intervention to work,
87-- without which, written traces are silently dropped/ignored and no output will
88-- be generated. The GHC eventlog documentation provides information about
89-- enabling, viewing and working with event traces at
90-- <http://hackage.haskell.org/trac/ghc/wiki/EventLog>.
92module Control.Distributed.Process.Debug
93 ( -- * Exported Data Types
97 -- * Configuring Tracing
113 -- * Sending Custom Trace Data
117 -- * Working with remote nodes
119 , Remote.startTraceRelay
120 , Remote.setTraceFlagsRemote
121 -- * Built in tracers
128import Control.Applicative ((<$>))
129import Control.Distributed.Process.Internal.Primitives
141import Control.Distributed.Process.Internal.Types
145 , ProcessMonitorNotification(..)
147import Control.Distributed.Process.Management.Types
150import Control.Distributed.Process.Management.Trace.Types
156import Control.Distributed.Process.Management.Trace.Tracer
161import Control.Distributed.Process.Management.Trace.Primitives
162 ( withRegisteredTracer
176import qualified Control.Distributed.Process.Management.Trace.Remote as Remote
177import Control.Distributed.Process.Node
179import Control.Exception (SomeException)
181import Control.Monad.IO.Class (liftIO)
182import Control.Monad.Reader (ask)
186#if ! MIN_VERSION_base(4,6,0)
187import Prelude hiding (catch)
190--------------------------------------------------------------------------------
191-- Debugging/Tracing API --
192--------------------------------------------------------------------------------
194-- | Starts a new tracer, using the supplied trace function.
195-- Only one tracer can be registered at a time, however /this/ function overlays
196-- the registered tracer with the supplied handler, allowing the user to layer
197-- multiple tracers on top of one another, with trace events forwarded down
198-- through all the layers in turn. Once the top layer is stopped, the user
199-- is responsible for re-registering the original (prior) tracer pid before
200-- terminating. See 'withTracer' for a mechanism that handles that.
201startTracer :: (MxEvent -> Process ()) -> Process ProcessId
202startTracer handler = do
203 withRegisteredTracer $ \pid -> do
204 node <- processNode <$> ask
205 newPid <- liftIO $ forkProcess node $ traceProxy pid handler
206 enableTrace newPid -- invokes sync + registration
209-- | Evaluate @proc@ with tracing enabled via @handler@, and immediately
210-- disable tracing thereafter, before giving the result (or exception
211-- in case of failure).
212withTracer :: forall a.
213 (MxEvent -> Process ())
215 -> Process (Either SomeException a)
216withTracer handler proc = do
217 previous <- whereis "tracer"
218 tracer <- startTracer handler
220 (stopTracing tracer previous)
222 stopTracing :: ProcessId -> Maybe ProcessId -> Process ()
223 stopTracing tracer previousTracer = do
224 case previousTracer of
227 ref <- monitor tracer
228 send tracer MxTraceDisable
230 matchIf (\(ProcessMonitorNotification ref' _ _) -> ref == ref')
234-- | Evaluate @proc@ with the supplied flags enabled. Any previously set
235-- trace flags are restored immediately afterwards.
236withFlags :: forall a.
239 -> Process (Either SomeException a)
240withFlags flags proc = do
241 oldFlags <- getTraceFlags
242 finally (setTraceFlags flags >> try proc)
243 (setTraceFlags oldFlags)
245traceProxy :: ProcessId -> (MxEvent -> Process ()) -> Process ()
246traceProxy pid act = do
247 proxy pid $ \(ev :: MxEvent) ->
249 (MxTraceTakeover _) -> return False
250 MxTraceDisable -> die "disabled"
251 _ -> act ev >> return True
253-- | Stops a user supplied tracer started with 'startTracer'.
254-- Note that only one tracer process can be active at any given time.
255-- This process will stop the last process started with 'startTracer'.
256-- If 'startTracer' is called multiple times, successive calls to this
257-- function will stop the tracers in the reverse order which they were
260-- This function will never stop the system tracer (i.e., the tracer
261-- initially started when the node is created), therefore once all user
262-- supplied tracers (i.e., processes started via 'startTracer') have exited,
263-- subsequent calls to this function will have no effect.
265-- If the last tracer to have been registered was not started
266-- with 'startTracer' then the behaviour of this function is /undefined/.
267stopTracer :: Process ()
269 withRegisteredTracer $ \pid -> do
270 -- we need to avoid killing the initial (base) tracer, as
271 -- nothing we rely on having exactly 1 registered tracer
272 -- process at all times.
273 basePid <- whereis "tracer.initial"
274 case basePid == (Just pid) of
276 False -> send pid MxTraceDisable
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list