Safe Haskell | None |
---|---|
Language | Haskell2010 |
Thread-safe logging with bonus controlled-schedule debugging capabilities.
This module supports logging to memory, serializing messages and deferring the work of actually printing them. Another thread can flush the logged messages at its leisure.
The second capability of this infrastructure is to use the debugging print messages
as points at which to gate the execution of the program. That is, each logOn
call becomes a place where the program blocks and checks in with a central
coordinator, which only allows one thread to unblock at a time. Thus, if there are
sufficient debug logging messages in the program, this can enable a form of
deterministic replay (and quickcheck-style testing of different interleavings).
This becomes most useful when there is a log message before each read and write
to shared memory.
Note that this library allows compile-time toggling of debug support. When it is compiled out, it should have no overhead. When it is compiled in, it will be controlled by an environment variable dynamically.
- dbgLvl :: Int
- newLogger :: (Int, Int) -> [OutDest] -> WaitMode -> IO Logger
- logStrLn :: Logger -> Int -> String -> IO ()
- logByteStringLn :: Logger -> Int -> ByteString -> IO ()
- logTextLn :: Logger -> Int -> Text -> IO ()
- logOn :: Logger -> LogMsg -> IO ()
- data Logger
- data WaitMode
- = WaitNum {
- numThreads :: Int
- downThreads :: IO Int
- | DontWait
- = WaitNum {
- data LogMsg
- data OutDest
- msgBody :: LogMsg -> String
Global variables
Debugging flag used by pure code to tell if debugging chatter should be active.
This is activated by setting the environment variable DEBUG=1..5
.
By convention DEBUG=100
turns on full sequentialization of the program and
control over the interleavings in concurrent code, enabling systematic debugging
of concurrency problems.
Basic Logger interface
:: (Int, Int) | What inclusive range of messages do we accept? Default is typically `(0,dbgLvl)`. |
-> [OutDest] | Where do we write debugging messages? |
-> WaitMode | Do we wait for workers before proceeding sequentially but randomly (fuzz testing event interleavings)? |
-> IO Logger |
Create a new logger, which includes forking a coordinator thread. Takes as argument the number of worker threads participating in the computation.
logStrLn :: Logger -> Int -> String -> IO () Source #
Log a string message at a given verbosity level.
logByteStringLn :: Logger -> Int -> ByteString -> IO () Source #
Log a bytestring message at a given verbosity level.
detailed interface.
logOn :: Logger -> LogMsg -> IO () Source #
Write a log message from the current thread, IF the level of the
message falls into the range accepted by the given Logger
,
otherwise, the message is ignored.
A Logger coordinates a set of threads that print debug logging messages.
Loggers are abstract objects supporting only the operations provided by this module and the non-hidden fields of the Logger data type.
Several different ways we know to wait for quiescence in the concurrent mutator before proceeding.
WaitNum | A fixed set of threads must check-in each round before proceeding. |
| |
DontWait | In this mode, logging calls are non-blocking and return immediately, rather than waiting on a central coordinator. This is what we want if we're simply printing debugging output, not controlling the schedule for stress testing. |
We allow logging in O(1) time in String format. In practice string conversions are not that important, because only *thunks* should be logged; the thread printing the logs should deal with forcing those thunks.
StrMsg | |
OffTheRecord | This sort of message is chatter and NOT meant to participate in the scheduler-testing framework. | ByteStrMsg { lvl::Int, bbody::ByteString } |
A destination for log messages
OutputEvents | Output via GHC's |
OutputTo Handle | Printed human-readable output to a handle. |
OutputInMemory | Accumulate output in memory and flush when appropriate. |