tslogger- Thread-safe logging, with additional interleaving fuzz-testing.

Safe HaskellNone




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.


Global variables

dbgLvl :: Int Source #

Debugging flag shared by several modules. 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.

defaultMemDbgRange :: (Int, Int) Source #

This codifies the convention of keeping fine-grained per-memory-modification messages at higher debug levels. These are used for fuzz testing concurrent interleavings. Setting dbgRange in the DbgCfg to this value should give you only the messages necessary for stress testing schedules.

Basic Logger interface

newLogger Source #


:: (Int, Int)

What inclusive range of messages do we accept? Defaults to `(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.

logTextLn :: Logger -> Int -> Text -> IO () Source #

Log a Text 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.

data Logger Source #

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.

data WaitMode Source #

Several different ways we know to wait for quiescence in the concurrent mutator before proceeding.



UNFINISHED: Dynamically track tasks/workers. The num workers starts at 1 and then is modified with incrTasks and decrTasks.


A fixed set of threads must check-in each round before proceeding.


  • numThreads :: Int

    How many threads total must check in?

  • downThreads :: IO Int

    Poll how many threads WON'T participate this round. After all *productive* threads have checked in this number must grow to eventually include all other threads.


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.

data LogMsg Source #

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.





This sort of message is chatter and NOT meant to participate in the scheduler-testing framework. | ByteStrMsg { lvl::Int, bbody::ByteString }


data OutDest Source #

A destination for log messages



Output via GHC's traceEvent runtime events.

OutputTo Handle

Printed human-readable output to a handle.


Accumulate output in memory and flush when appropriate.


msgBody :: LogMsg -> String Source #

Convert just the body of the log message to a string.

Detailed configuration control

data DbgCfg Source #

DebugConfig: what level of debugging support is activated?




  • dbgRange :: Maybe (Int, Int)

    Inclusive range of debug messages to accept (i.e. filter on priority level). If Nothing, use the default level, which is (0,N) where N is controlled by the DEBUG environment variable. The convention is to use Just (0,0) to disable logging.

  • dbgDests :: [OutDest]

    Destinations for debug log messages.

  • dbgScheduling :: Bool

    In additional to logging debug messages, control thread interleaving at these points when this is True.