-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
452 additions
and
395 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,6 @@ | ||
# `niio`: functions for debugging that guarantee non-interleaved output | ||
# `provenance`: utilities for tracking provenance | ||
|
||
This package provides analogues of various standard output functions; all of | ||
these functions are safe to use in a concurrent setting, and guarantee that the | ||
output of one function call will not be interleaved with the output of another. | ||
Examples include | ||
This package provides utilities that can help track when and where things are | ||
defined, to aid debugging. It also provides some utilities for non-interleaved | ||
output, for debugging concurrent code. | ||
|
||
```haskell | ||
niPutStr :: MonadIO m => String -> m () | ||
niPutStrLn :: MonadIO m => String -> m () | ||
niPrint :: (MonadIO m, Show a) => a -> m () | ||
niTrace :: String -> a -> a | ||
niTraceShow :: Show a => a -> b -> b | ||
niTraceShowId :: Show a => a -> a | ||
niTraceM :: Applicative m => String -> m () | ||
niTraceShowM :: (Applicative m, Show a) => a -> m () | ||
``` | ||
|
||
In addition, we provide some support for creating uniques, to be able to | ||
support correlating log messages, and some functionality for working with | ||
these uniques: | ||
|
||
```haskell | ||
niGetUnique :: (MonadIO m, HasCallStack) => m NiUnique | ||
niPutStrAt :: MonadIO m => [NiUnique] -> String -> m () | ||
``` | ||
|
||
and | ||
|
||
```haskell | ||
niBracket :: | ||
(MonadIO m, MonadMask m, HasCallStack) | ||
=> (NiUnique -> m ()) -- ^ Prior to the action | ||
-> (NiUnique -> ExitCase a -> m ()) -- ^ After | ||
-> (NiUnique -> m a) | ||
-> m a | ||
``` | ||
|
||
For example: | ||
|
||
```haskell | ||
-- > niBracket (\i -> niPutStrAt [i] "start") (\i -> niPutStrAt [i] . show) $ \i -> | ||
-- > .. | ||
-- > niBracket (\j -> niPutStrAt [i, j] "start") (\j -> niPutStrAt [i, j] . show) $ \j -> | ||
-- > .. | ||
-- > niPutStrAt [i, j] $ "foo: " ++ E.displayException e | ||
-- > .. | ||
``` | ||
|
||
might result in | ||
|
||
``` | ||
-- > ["exampleFun(./Example/File.hs:100:5)/1"] start | ||
-- > ["exampleFun(./Example/File.hs:100:5)/1","exampleFun(./Example/File.hs:120:13)/1"] start | ||
-- > ["exampleFun(./Example/File.hs:100:5)/1","exampleFun(./Example/File.hs:120:13)/1"] | ||
-- > foo: ExampleException | ||
-- > HasCallStack backtrace: | ||
-- > collectBacktraces, called at (..) | ||
-- > toExceptionWithBacktrace, called at (..) | ||
-- > throwIO, called at (..) | ||
-- > ["exampleFun(./Example/File.hs:100:5)/1","exampleFun(./Example/File.hs:120:13)/1"] ExitCaseSuccess () | ||
-- > ["exampleFun(./Example/File.hs:100:5)/1"] ExitCaseSuccess () | ||
``` | ||
|
||
This package is intended for debugging only. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
-- | Functions for non-interleaved output | ||
-- | ||
-- Intended for qualifed import. | ||
-- | ||
-- > import qualified Debug.NonInterleavedIO as NIIO | ||
-- | ||
-- Alternatively, you can import "Debug.NonInterleavedIO.Trace" as a drop-in | ||
-- replacement for "Debug.Trace". | ||
-- | ||
-- The functions in this module can all be called concurrently, without | ||
-- resulting in interleaved output: each function call is atomic. | ||
-- | ||
-- The first time any of these functions is called, we lookup the @NIIO_OUTPUT@ | ||
-- environment variable. If set, we will write to the file specified (if the | ||
-- file already exists, it will be overwritten). If @NIIO_OUTPUT@ is not set, a | ||
-- temporary file will be created in the system temporary directory; typically | ||
-- such a file will be called @/tmp/niio<number>@. The name of this file is | ||
-- written to @stderr@ (this is the /only/ output origiating from functions in | ||
-- this module that is not written to the file). | ||
module Debug.NonInterleavedIO ( | ||
-- * Output functions | ||
putStr | ||
, putStrLn | ||
-- * Tracing functions | ||
, trace | ||
, traceShow | ||
, traceShowId | ||
, traceM | ||
, traceShowM | ||
) where | ||
|
||
import Prelude hiding (putStr, putStrLn, print) | ||
|
||
import Control.Concurrent | ||
import Control.Monad.IO.Class | ||
import System.Environment | ||
import System.IO.Temp (getCanonicalTemporaryDirectory) | ||
import System.IO.Unsafe | ||
|
||
import qualified System.IO as IO | ||
|
||
{------------------------------------------------------------------------------- | ||
Output functions | ||
-------------------------------------------------------------------------------} | ||
|
||
-- | Non-interleaved version of 'Prelude.putStr' | ||
putStr :: MonadIO m => String -> m () | ||
putStr str = liftIO $ withMVar globalHandle $ \h -> IO.hPutStr h str | ||
|
||
-- | Non-interleaved version of 'Prelude.putStrLn' | ||
putStrLn :: MonadIO m => String -> m () | ||
putStrLn = putStr . (++ "\n") | ||
|
||
-- | Non-interleaved version of 'Prelude.print' | ||
print :: MonadIO m => Show a => a -> m () | ||
print = putStrLn . show | ||
|
||
{------------------------------------------------------------------------------- | ||
Tracing | ||
-------------------------------------------------------------------------------} | ||
|
||
-- | Non-interleaved version of 'Debug.Trace.trace' | ||
trace :: String -> a -> a | ||
trace str a = unsafePerformIO $ putStrLn str >> return a | ||
|
||
-- | Non-interleaved version of 'Debug.Trace.traceShow' | ||
traceShow :: Show a => a -> b -> b | ||
traceShow = trace . show | ||
|
||
-- | Non-interleaved version of 'Debug.Trace.traceShowId' | ||
traceShowId :: Show a => a -> a | ||
traceShowId a = traceShow (show a) a | ||
|
||
-- | Non-interleaved version of 'Debug.Trace.traceM' | ||
traceM :: Applicative m => String -> m () | ||
traceM str = trace str $ pure () | ||
|
||
-- | Non-interleaved version of 'Debug.Trace.traceShowM' | ||
traceShowM :: (Applicative m, Show a) => a -> m () | ||
traceShowM = traceM . show | ||
|
||
{------------------------------------------------------------------------------- | ||
Internal: globals | ||
-------------------------------------------------------------------------------} | ||
|
||
globalHandle :: MVar IO.Handle | ||
{-# NOINLINE globalHandle #-} | ||
globalHandle = unsafePerformIO $ do | ||
mOutput <- lookupEnv "NIIO_OUTPUT" | ||
(fp, h) <- case mOutput of | ||
Nothing -> do | ||
tmpDir <- getCanonicalTemporaryDirectory | ||
IO.openTempFile tmpDir "niio" | ||
Just fp -> do | ||
(fp,) <$> IO.openFile fp IO.WriteMode | ||
IO.hPutStrLn IO.stderr $ "niio output to " ++ fp | ||
IO.hFlush IO.stderr | ||
IO.hSetBuffering h IO.NoBuffering | ||
newMVar h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{-# OPTIONS_GHC -Wno-orphans #-} | ||
|
||
-- | Utilities for writing debugging messages that include provenance | ||
-- | ||
-- Intended for qualified import. | ||
-- | ||
-- > import qualified Debug.NonInterleavedIO.Scoped as Scoped | ||
module Debug.NonInterleavedIO.Scoped ( | ||
putStrLn | ||
) where | ||
|
||
import Prelude hiding (putStrLn) | ||
|
||
import Control.Monad.IO.Class | ||
import Data.List (intercalate) | ||
import GHC.Stack | ||
|
||
import Debug.Provenance | ||
import Debug.Provenance.Scope | ||
|
||
import qualified Debug.NonInterleavedIO as NIIO | ||
|
||
{------------------------------------------------------------------------------- | ||
Uniques | ||
-------------------------------------------------------------------------------} | ||
|
||
-- | Print debug message, showing current scope | ||
putStrLn :: MonadIO m => String -> m () | ||
putStrLn str = withFrozenCallStack $ do | ||
scope <- getScope | ||
here <- newInvocation callSite | ||
|
||
let prettyScope :: String | ||
prettyScope = intercalate "," $ map prettyInvocation (here : scope) | ||
|
||
NIIO.putStrLn $ | ||
case lines str of | ||
[one] -> prettyScope ++ " " ++ one | ||
many -> intercalate "\n" $ prettyScope : map (" " ++) many |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
-- | Drop-in replacement for "Debug.Trace" | ||
-- | ||
-- This module just re-exports some functions from "Debug.NonInterleavedIO"; | ||
-- since it does not export anything that clashes with "Prelude" it can be | ||
-- imported unqualified. | ||
module Debug.NonInterleavedIO.Trace ( | ||
trace | ||
, traceShow | ||
, traceShowId | ||
, traceM | ||
, traceShowM | ||
) where | ||
|
||
import Debug.NonInterleavedIO |
Oops, something went wrong.