summaryrefslogtreecommitdiff
path: root/compiler/GHC/Utils/Logger.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Utils/Logger.hs')
-rw-r--r--compiler/GHC/Utils/Logger.hs325
1 files changed, 223 insertions, 102 deletions
diff --git a/compiler/GHC/Utils/Logger.hs b/compiler/GHC/Utils/Logger.hs
index 164aa4d387..77506682bd 100644
--- a/compiler/GHC/Utils/Logger.hs
+++ b/compiler/GHC/Utils/Logger.hs
@@ -1,20 +1,34 @@
{-# LANGUAGE RankNTypes #-}
-- | Logger
+--
+-- The Logger is an configurable entity that is used by the compiler to output
+-- messages on the console (stdout, stderr) and in dump files.
+--
+-- The behaviour of default Logger returned by `initLogger` can be modified with
+-- hooks. The compiler itself uses hooks in multithreaded code (--make) and it
+-- is also probably used by ghc-api users (IDEs, etc.).
+--
+-- In addition to hooks, the Logger suppors LogFlags: basically a subset of the
+-- command-line flags that control the logger behaviour at a higher level than
+-- hooks.
+--
+-- 1. Hooks are used to define how to generate a info/warning/error/dump messages
+-- 2. LogFlags are used to decide when and how to generate messages
+--
module GHC.Utils.Logger
( Logger
- , initLogger
, HasLogger (..)
, ContainsLogger (..)
+
+ -- * Logger setup
+ , initLogger
, LogAction
, DumpAction
, TraceAction
, DumpFormat (..)
- , putLogMsg
- , putDumpMsg
- , putTraceMsg
- -- * Hooks
+ -- ** Hooks
, popLogHook
, pushLogHook
, popDumpHook
@@ -23,27 +37,45 @@ module GHC.Utils.Logger
, pushTraceHook
, makeThreadSafe
+ -- ** Flags
+ , LogFlags (..)
+ , defaultLogFlags
+ , log_dopt
+ , log_set_dopt
+ , setLogFlags
+ , updateLogFlags
+ , logFlags
+ , logHasDumpFlag
+ , logVerbAtLeast
+
-- * Logging
, jsonLogAction
+ , putLogMsg
, defaultLogAction
, defaultLogActionHPrintDoc
, defaultLogActionHPutStrDoc
+ , logMsg
+ , logDumpMsg
-- * Dumping
, defaultDumpAction
+ , putDumpFile
+ , putDumpFileMaybe
+ , putDumpFileMaybe'
, withDumpFileHandle
, touchDumpFile
- , dumpIfSet
- , dumpIfSet_dyn
- , dumpIfSet_dyn_printer
+ , logDumpFile
-- * Tracing
, defaultTraceAction
+ , putTraceMsg
+ , loggerTraceFlushUpdate
+ , loggerTraceFlush
+ , logTraceMsg
)
where
import GHC.Prelude
-import GHC.Driver.Session
import GHC.Driver.Flags
import GHC.Driver.Ppr
import GHC.Types.Error
@@ -54,6 +86,9 @@ import GHC.Utils.Outputable
import GHC.Utils.Json
import GHC.Utils.Panic
+import GHC.Data.EnumSet (EnumSet)
+import qualified GHC.Data.EnumSet as EnumSet
+
import Data.IORef
import System.Directory
import System.FilePath ( takeDirectory, (</>) )
@@ -67,13 +102,79 @@ import Control.Monad
import Control.Concurrent.MVar
import System.IO.Unsafe
-type LogAction = DynFlags
+---------------------------------------------------------------
+-- Log flags
+---------------------------------------------------------------
+
+-- | Logger flags
+data LogFlags = LogFlags
+ { log_default_user_context :: SDocContext
+ , log_default_dump_context :: SDocContext
+ , log_dump_flags :: !(EnumSet DumpFlag) -- ^ Dump flags
+ , log_show_caret :: !Bool -- ^ Show caret in diagnostics
+ , log_show_warn_groups :: !Bool -- ^ Show warning flag groups
+ , log_enable_timestamps :: !Bool -- ^ Enable timestamps
+ , log_dump_to_file :: !Bool -- ^ Enable dump to file
+ , log_dump_dir :: !(Maybe FilePath) -- ^ Dump directory
+ , log_dump_prefix :: !(Maybe FilePath) -- ^ Normal dump path ("basename.")
+ , log_dump_prefix_override :: !(Maybe FilePath) -- ^ Overriden dump path
+ , log_enable_debug :: !Bool -- ^ Enable debug output
+ , log_verbosity :: !Int -- ^ Verbosity level
+ }
+
+-- | Default LogFlags
+defaultLogFlags :: LogFlags
+defaultLogFlags = LogFlags
+ { log_default_user_context = defaultSDocContext
+ , log_default_dump_context = defaultSDocContext
+ , log_dump_flags = EnumSet.empty
+ , log_show_caret = True
+ , log_show_warn_groups = True
+ , log_enable_timestamps = True
+ , log_dump_to_file = False
+ , log_dump_dir = Nothing
+ , log_dump_prefix = Nothing
+ , log_dump_prefix_override = Nothing
+ , log_enable_debug = False
+ , log_verbosity = 0
+ }
+
+-- | Test if a DumpFlag is enabled
+log_dopt :: DumpFlag -> LogFlags -> Bool
+log_dopt f logflags = f `EnumSet.member` log_dump_flags logflags
+
+-- | Enable a DumpFlag
+log_set_dopt :: DumpFlag -> LogFlags -> LogFlags
+log_set_dopt f logflags = logflags { log_dump_flags = EnumSet.insert f (log_dump_flags logflags) }
+
+-- | Test if a DumpFlag is set
+logHasDumpFlag :: Logger -> DumpFlag -> Bool
+logHasDumpFlag logger f = log_dopt f (logFlags logger)
+
+-- | Test if verbosity is >= to the given value
+logVerbAtLeast :: Logger -> Int -> Bool
+logVerbAtLeast logger v = log_verbosity (logFlags logger) >= v
+
+-- | Update LogFlags
+updateLogFlags :: Logger -> (LogFlags -> LogFlags) -> Logger
+updateLogFlags logger f = setLogFlags logger (f (logFlags logger))
+
+-- | Set LogFlags
+setLogFlags :: Logger -> LogFlags -> Logger
+setLogFlags logger flags = logger { logFlags = flags }
+
+
+---------------------------------------------------------------
+-- Logger
+---------------------------------------------------------------
+
+type LogAction = LogFlags
-> MessageClass
-> SrcSpan
-> SDoc
-> IO ()
-type DumpAction = DynFlags
+type DumpAction = LogFlags
-> PprStyle
-> DumpFlag
-> String
@@ -81,7 +182,7 @@ type DumpAction = DynFlags
-> SDoc
-> IO ()
-type TraceAction a = DynFlags -> String -> SDoc -> a -> a
+type TraceAction a = LogFlags -> String -> SDoc -> a -> a
-- | Format of a dump
--
@@ -114,8 +215,28 @@ data Logger = Logger
, generated_dumps :: DumpCache
-- ^ Already dumped files (to append instead of overwriting them)
+
+ , trace_flush :: IO ()
+ -- ^ Flush the trace buffer
+
+ , logFlags :: !LogFlags
+ -- ^ Logger flags
}
+-- | Set the trace flushing function
+--
+-- The currently set trace flushing function is passed to the updating function
+loggerTraceFlushUpdate :: Logger -> (IO () -> IO ()) -> Logger
+loggerTraceFlushUpdate logger upd = logger { trace_flush = upd (trace_flush logger) }
+
+-- | Calls the trace flushing function
+loggerTraceFlush :: Logger -> IO ()
+loggerTraceFlush logger = trace_flush logger
+
+-- | Default trace flushing function (flush stderr)
+defaultTraceFlush :: IO ()
+defaultTraceFlush = hFlush stderr
+
initLogger :: IO Logger
initLogger = do
dumps <- newIORef Set.empty
@@ -124,6 +245,8 @@ initLogger = do
, dump_hook = []
, trace_hook = []
, generated_dumps = dumps
+ , trace_flush = defaultTraceFlush
+ , logFlags = defaultLogFlags
}
-- | Log something
@@ -131,8 +254,8 @@ putLogMsg :: Logger -> LogAction
putLogMsg logger = foldr ($) defaultLogAction (log_hook logger)
-- | Dump something
-putDumpMsg :: Logger -> DumpAction
-putDumpMsg logger =
+putDumpFile :: Logger -> DumpAction
+putDumpFile logger =
let
fallback = putLogMsg logger
dumps = generated_dumps logger
@@ -182,15 +305,15 @@ makeThreadSafe logger = do
with_lock :: forall a. IO a -> IO a
with_lock act = withMVar lock (const act)
- log action dflags msg_class loc doc =
- with_lock (action dflags msg_class loc doc)
+ log action logflags msg_class loc doc =
+ with_lock (action logflags msg_class loc doc)
- dmp action dflags sty opts str fmt doc =
- with_lock (action dflags sty opts str fmt doc)
+ dmp action logflags sty opts str fmt doc =
+ with_lock (action logflags sty opts str fmt doc)
trc :: forall a. TraceAction a -> TraceAction a
- trc action dflags str doc v =
- unsafePerformIO (with_lock (return $! action dflags str doc v))
+ trc action logflags str doc v =
+ unsafePerformIO (with_lock (return $! action logflags str doc v))
return $ pushLogHook log
$ pushDumpHook dmp
@@ -201,12 +324,12 @@ makeThreadSafe logger = do
--
jsonLogAction :: LogAction
jsonLogAction _ (MCDiagnostic SevIgnore _) _ _ = return () -- suppress the message
-jsonLogAction dflags msg_class srcSpan msg
+jsonLogAction logflags msg_class srcSpan msg
=
- defaultLogActionHPutStrDoc dflags True stdout
+ defaultLogActionHPutStrDoc logflags True stdout
(withPprStyle (PprCode CStyle) (doc $$ text ""))
where
- str = renderWithContext (initSDocContext dflags defaultUserStyle) msg
+ str = renderWithContext (log_default_user_context logflags) msg
doc = renderJSON $
JSObject [ ( "span", json srcSpan )
, ( "doc" , JSString str )
@@ -214,8 +337,8 @@ jsonLogAction dflags msg_class srcSpan msg
]
defaultLogAction :: LogAction
-defaultLogAction dflags msg_class srcSpan msg
- | dopt Opt_D_dump_json dflags = jsonLogAction dflags msg_class srcSpan msg
+defaultLogAction logflags msg_class srcSpan msg
+ | log_dopt Opt_D_dump_json logflags = jsonLogAction logflags msg_class srcSpan msg
| otherwise = case msg_class of
MCOutput -> printOut msg
MCDump -> printOut (msg $$ blankLine)
@@ -225,16 +348,16 @@ defaultLogAction dflags msg_class srcSpan msg
MCDiagnostic SevIgnore _ -> pure () -- suppress the message
MCDiagnostic sev rea -> printDiagnostics sev rea
where
- printOut = defaultLogActionHPrintDoc dflags False stdout
- printErrs = defaultLogActionHPrintDoc dflags False stderr
- putStrSDoc = defaultLogActionHPutStrDoc dflags False stdout
+ printOut = defaultLogActionHPrintDoc logflags False stdout
+ printErrs = defaultLogActionHPrintDoc logflags False stderr
+ putStrSDoc = defaultLogActionHPutStrDoc logflags False stdout
-- Pretty print the warning flag, if any (#10752)
message sev rea = mkLocMessageAnn (flagMsg sev rea) msg_class srcSpan msg
printDiagnostics severity reason = do
hPutChar stderr '\n'
caretDiagnostic <-
- if gopt Opt_DiagnosticsShowCaret dflags
+ if log_show_caret logflags
then getCaretDiagnostic msg_class srcSpan
else pure empty
printErrs $ getPprStyle $ \style ->
@@ -262,26 +385,24 @@ defaultLogAction dflags msg_class srcSpan msg
panic "SevWarning with ErrorWithoutFlag"
warnFlagGrp flag
- | gopt Opt_ShowWarnGroups dflags =
+ | log_show_warn_groups logflags =
case smallestWarningGroups flag of
[] -> ""
groups -> " (in " ++ intercalate ", " (map ("-W"++) groups) ++ ")"
| otherwise = ""
-- | Like 'defaultLogActionHPutStrDoc' but appends an extra newline.
-defaultLogActionHPrintDoc :: DynFlags -> Bool -> Handle -> SDoc -> IO ()
-defaultLogActionHPrintDoc dflags asciiSpace h d
- = defaultLogActionHPutStrDoc dflags asciiSpace h (d $$ text "")
+defaultLogActionHPrintDoc :: LogFlags -> Bool -> Handle -> SDoc -> IO ()
+defaultLogActionHPrintDoc logflags asciiSpace h d
+ = defaultLogActionHPutStrDoc logflags asciiSpace h (d $$ text "")
-- | The boolean arguments let's the pretty printer know if it can optimize indent
-- by writing ascii ' ' characters without going through decoding.
-defaultLogActionHPutStrDoc :: DynFlags -> Bool -> Handle -> SDoc -> IO ()
-defaultLogActionHPutStrDoc dflags asciiSpace h d
+defaultLogActionHPutStrDoc :: LogFlags -> Bool -> Handle -> SDoc -> IO ()
+defaultLogActionHPutStrDoc logflags asciiSpace h d
-- Don't add a newline at the end, so that successive
-- calls to this log-action can output all on the same line
- = printSDoc ctx (Pretty.PageMode asciiSpace) h d
- where
- ctx = initSDocContext dflags defaultUserStyle
+ = printSDoc (log_default_dump_context logflags) (Pretty.PageMode asciiSpace) h d
--
-- Note [JSON Error Messages]
@@ -301,8 +422,8 @@ defaultLogActionHPutStrDoc dflags asciiSpace h d
-- | Default action for 'dumpAction' hook
defaultDumpAction :: DumpCache -> LogAction -> DumpAction
-defaultDumpAction dumps log_action dflags sty flag title _fmt doc =
- dumpSDocWithStyle dumps log_action sty dflags flag title doc
+defaultDumpAction dumps log_action logflags sty flag title _fmt doc =
+ dumpSDocWithStyle dumps log_action sty logflags flag title doc
-- | Write out a dump.
--
@@ -311,38 +432,37 @@ defaultDumpAction dumps log_action dflags sty flag title _fmt doc =
--
-- When @hdr@ is empty, we print in a more compact format (no separators and
-- blank lines)
-dumpSDocWithStyle :: DumpCache -> LogAction -> PprStyle -> DynFlags -> DumpFlag -> String -> SDoc -> IO ()
-dumpSDocWithStyle dumps log_action sty dflags flag hdr doc =
- withDumpFileHandle dumps dflags flag writeDump
+dumpSDocWithStyle :: DumpCache -> LogAction -> PprStyle -> LogFlags -> DumpFlag -> String -> SDoc -> IO ()
+dumpSDocWithStyle dumps log_action sty logflags flag hdr doc =
+ withDumpFileHandle dumps logflags flag writeDump
where
-- write dump to file
writeDump (Just handle) = do
doc' <- if null hdr
then return doc
- else do t <- getCurrentTime
- let timeStamp = if (gopt Opt_SuppressTimestamps dflags)
- then empty
- else text (show t)
+ else do timeStamp <- if log_enable_timestamps logflags
+ then (text . show) <$> getCurrentTime
+ else pure empty
let d = timeStamp
$$ blankLine
$$ doc
return $ mkDumpDoc hdr d
-- When we dump to files we use UTF8. Which allows ascii spaces.
- defaultLogActionHPrintDoc dflags True handle (withPprStyle sty doc')
+ defaultLogActionHPrintDoc logflags True handle (withPprStyle sty doc')
-- write the dump to stdout
writeDump Nothing = do
let (doc', msg_class)
| null hdr = (doc, MCOutput)
| otherwise = (mkDumpDoc hdr doc, MCDump)
- log_action dflags msg_class noSrcSpan (withPprStyle sty doc')
+ log_action logflags msg_class noSrcSpan (withPprStyle sty doc')
-- | Run an action with the handle of a 'DumpFlag' if we are outputting to a
-- file, otherwise 'Nothing'.
-withDumpFileHandle :: DumpCache -> DynFlags -> DumpFlag -> (Maybe Handle -> IO ()) -> IO ()
-withDumpFileHandle dumps dflags flag action = do
- let mFile = chooseDumpFile dflags flag
+withDumpFileHandle :: DumpCache -> LogFlags -> DumpFlag -> (Maybe Handle -> IO ()) -> IO ()
+withDumpFileHandle dumps logflags flag action = do
+ let mFile = chooseDumpFile logflags flag
case mFile of
Just fileName -> do
gd <- readIORef dumps
@@ -361,10 +481,10 @@ withDumpFileHandle dumps dflags flag action = do
action (Just handle)
Nothing -> action Nothing
--- | Choose where to put a dump file based on DynFlags and DumpFlag
-chooseDumpFile :: DynFlags -> DumpFlag -> Maybe FilePath
-chooseDumpFile dflags flag
- | gopt Opt_DumpToFile dflags || forced_to_file
+-- | Choose where to put a dump file based on LogFlags and DumpFlag
+chooseDumpFile :: LogFlags -> DumpFlag -> Maybe FilePath
+chooseDumpFile logflags flag
+ | log_dump_to_file logflags || forced_to_file
, Just prefix <- getPrefix
= Just $ setDir (prefix ++ dump_suffix)
@@ -389,27 +509,46 @@ chooseDumpFile dflags flag
getPrefix
-- dump file location is being forced
-- by the --ddump-file-prefix flag.
- | Just prefix <- dumpPrefixForce dflags
+ | Just prefix <- log_dump_prefix_override logflags
= Just prefix
-- dump file location chosen by GHC.Driver.Pipeline.runPipeline
- | Just prefix <- dumpPrefix dflags
+ | Just prefix <- log_dump_prefix logflags
= Just prefix
-- we haven't got a place to put a dump file.
| otherwise
= Nothing
- setDir f = case dumpDir dflags of
+ setDir f = case log_dump_dir logflags of
Just d -> d </> f
Nothing -> f
--- | This is a helper for 'dumpIfSet' to ensure that it's not duplicated
--- despite the fact that 'dumpIfSet' has an @INLINE@.
-doDump :: Logger -> DynFlags -> String -> SDoc -> IO ()
-doDump logger dflags hdr doc =
- putLogMsg logger dflags
- MCDump
- noSrcSpan
- (withPprStyle defaultDumpStyle
- (mkDumpDoc hdr doc))
+
+
+-- | Default action for 'traceAction' hook
+defaultTraceAction :: TraceAction a
+defaultTraceAction logflags title doc x =
+ if not (log_enable_debug logflags)
+ then x
+ else trace (showSDocDump (log_default_dump_context logflags)
+ (sep [text title, nest 2 doc])) x
+
+
+-- | Log something
+logMsg :: Logger -> MessageClass -> SrcSpan -> SDoc -> IO ()
+logMsg logger mc loc msg = putLogMsg logger (logFlags logger) mc loc msg
+
+-- | Dump something
+logDumpFile :: Logger -> PprStyle -> DumpFlag -> String -> DumpFormat -> SDoc -> IO ()
+logDumpFile logger = putDumpFile logger (logFlags logger)
+
+-- | Log a trace message
+logTraceMsg :: Logger -> String -> SDoc -> a -> a
+logTraceMsg logger hdr doc a = putTraceMsg logger (logFlags logger) hdr doc a
+
+-- | Log a dump message (not a dump file)
+logDumpMsg :: Logger -> String -> SDoc -> IO ()
+logDumpMsg logger hdr doc = logMsg logger MCDump noSrcSpan
+ (withPprStyle defaultDumpStyle
+ (mkDumpDoc hdr doc))
mkDumpDoc :: String -> SDoc -> SDoc
mkDumpDoc hdr doc
@@ -421,50 +560,32 @@ mkDumpDoc hdr doc
line = text "===================="
-dumpIfSet :: Logger -> DynFlags -> Bool -> String -> SDoc -> IO ()
-dumpIfSet logger dflags flag hdr doc
- | not flag = return ()
- | otherwise = doDump logger dflags hdr doc
-{-# INLINE dumpIfSet #-} -- see Note [INLINE conditional tracing utilities]
-
--- | A wrapper around 'dumpAction'.
--- First check whether the dump flag is set
--- Do nothing if it is unset
-dumpIfSet_dyn :: Logger -> DynFlags -> DumpFlag -> String -> DumpFormat -> SDoc -> IO ()
-dumpIfSet_dyn = dumpIfSet_dyn_printer alwaysQualify
-{-# INLINE dumpIfSet_dyn #-} -- see Note [INLINE conditional tracing utilities]
+-- | Dump if the given DumpFlag is set
+putDumpFileMaybe :: Logger -> DumpFlag -> String -> DumpFormat -> SDoc -> IO ()
+putDumpFileMaybe logger = putDumpFileMaybe' logger alwaysQualify
+{-# INLINE putDumpFileMaybe #-} -- see Note [INLINE conditional tracing utilities]
--- | A wrapper around 'putDumpMsg'.
--- First check whether the dump flag is set
--- Do nothing if it is unset
+-- | Dump if the given DumpFlag is set
--
--- Unlike 'dumpIfSet_dyn', has a printer argument
-dumpIfSet_dyn_printer
- :: PrintUnqualified
- -> Logger
- -> DynFlags
+-- Unlike 'putDumpFileMaybe', has a PrintUnqualified argument
+putDumpFileMaybe'
+ :: Logger
+ -> PrintUnqualified
-> DumpFlag
-> String
-> DumpFormat
-> SDoc
-> IO ()
-dumpIfSet_dyn_printer printer logger dflags flag hdr fmt doc
- = when (dopt flag dflags) $ do
+putDumpFileMaybe' logger printer flag hdr fmt doc
+ = when (logHasDumpFlag logger flag) $ do
let sty = mkDumpStyle printer
- putDumpMsg logger dflags sty flag hdr fmt doc
-{-# INLINE dumpIfSet_dyn_printer #-} -- see Note [INLINE conditional tracing utilities]
+ logDumpFile logger sty flag hdr fmt doc
+{-# INLINE putDumpFileMaybe' #-} -- see Note [INLINE conditional tracing utilities]
-- | Ensure that a dump file is created even if it stays empty
-touchDumpFile :: Logger -> DynFlags -> DumpFlag -> IO ()
-touchDumpFile logger dflags flag =
- withDumpFileHandle (generated_dumps logger) dflags flag (const (return ()))
-
-
--- | Default action for 'traceAction' hook
-defaultTraceAction :: TraceAction a
-defaultTraceAction dflags title doc = pprTraceWithFlags dflags title doc
-
-
+touchDumpFile :: Logger -> DumpFlag -> IO ()
+touchDumpFile logger flag =
+ withDumpFileHandle (generated_dumps logger) (logFlags logger) flag (const (return ()))
class HasLogger m where
getLogger :: m Logger