@@ -77,6 +77,7 @@ module Development.IDE.Core.Shake(
7777 addPersistentRule ,
7878 garbageCollectDirtyKeys ,
7979 garbageCollectDirtyKeysOlderThan ,
80+ Log
8081 ) where
8182
8283import Control.Concurrent.Async
@@ -149,6 +150,7 @@ import Language.LSP.Types.Capabilities
149150import OpenTelemetry.Eventlog
150151
151152import Control.Concurrent.STM.Stats (atomicallyNamed )
153+ import Control.Exception.Base (SomeException (SomeException ))
152154import Control.Exception.Extra hiding (bracket_ )
153155import Data.Aeson (toJSON )
154156import qualified Data.ByteString.Char8 as BS8
@@ -160,6 +162,7 @@ import qualified Data.HashSet as HSet
160162import Data.String (fromString )
161163import Data.Text (pack )
162164import Debug.Trace.Flags (userTracingEnabled )
165+ import Development.IDE.Types.Action (DelayedActionInternal )
163166import qualified Development.IDE.Types.Exports as ExportsMap
164167import qualified Focus
165168import HieDb.Types
@@ -169,6 +172,16 @@ import Ide.Types (PluginId)
169172import qualified "list-t" ListT
170173import qualified StmContainers.Map as STM
171174
175+ data Log
176+ = LogCreateHieDbExportsMapStart
177+ -- logDebug logger "Initializing exports map from hiedb"
178+ | LogCreateHieDbExportsMapFinish ! Int
179+ -- logDebug logger $ "Done initializing exports map from hiedb (" <> pack(show (ExportsMap.size em)) <> ")"
180+ | LogBuildSessionRestart ! String ! [DelayedActionInternal ] ! (HashSet Key ) ! Seconds ! (Maybe FilePath )
181+ | LogDelayedAction ! (DelayedAction () ) ! Seconds
182+ | LogBuildSessionFinish ! (Maybe SomeException )
183+ deriving Show
184+
172185-- | We need to serialize writes to the database, so we send any function that
173186-- needs to write to the database over the channel, where it will be picked up by
174187-- a worker thread.
@@ -494,7 +507,8 @@ seqValue val = case val of
494507 Failed _ -> val
495508
496509-- | Open a 'IdeState', should be shut using 'shakeShut'.
497- shakeOpen :: Maybe (LSP. LanguageContextEnv Config )
510+ shakeOpen :: Recorder Log
511+ -> Maybe (LSP. LanguageContextEnv Config )
498512 -> Config
499513 -> Logger
500514 -> Debouncer NormalizedUri
@@ -507,8 +521,10 @@ shakeOpen :: Maybe (LSP.LanguageContextEnv Config)
507521 -> ShakeOptions
508522 -> Rules ()
509523 -> IO IdeState
510- shakeOpen lspEnv defaultConfig logger debouncer
524+ shakeOpen recorder lspEnv defaultConfig logger debouncer
511525 shakeProfileDir (IdeReportProgress reportProgress) ideTesting@ (IdeTesting testing) withHieDb indexQueue vfs opts rules = mdo
526+ let log :: Log -> IO ()
527+ log = logWith recorder
512528
513529 us <- mkSplitUniqSupply ' r'
514530 ideNc <- newIORef (initNameCache us knownKeyNames)
@@ -520,19 +536,20 @@ shakeOpen lspEnv defaultConfig logger debouncer
520536 publishedDiagnostics <- STM. newIO
521537 positionMapping <- STM. newIO
522538 knownTargetsVar <- newTVarIO $ hashed HMap. empty
523- let restartShakeSession = shakeRestart ideState
539+ let restartShakeSession = shakeRestart recorder ideState
524540 persistentKeys <- newTVarIO HMap. empty
525541 indexPending <- newTVarIO HMap. empty
526542 indexCompleted <- newTVarIO 0
527543 indexProgressToken <- newVar Nothing
528544 let hiedbWriter = HieDbWriter {.. }
529545 exportsMap <- newTVarIO mempty
530546 -- lazily initialize the exports map with the contents of the hiedb
547+ -- TODO: exceptions can be swallowed here?
531548 _ <- async $ do
532- logDebug logger " Initializing exports map from hiedb "
549+ log LogCreateHieDbExportsMapStart
533550 em <- createExportsMapHieDb withHieDb
534551 atomically $ modifyTVar' exportsMap (<> em)
535- logDebug logger $ " Done initializing exports map from hiedb ( " <> pack( show ( ExportsMap. size em)) <> " ) "
552+ log $ LogCreateHieDbExportsMapFinish ( ExportsMap. size em)
536553
537554 progress <- do
538555 let (before, after) = if testing then (0 ,0.1 ) else (0.1 ,0.1 )
@@ -584,9 +601,9 @@ startTelemetry db extras@ShakeExtras{..}
584601
585602
586603-- | Must be called in the 'Initialized' handler and only once
587- shakeSessionInit :: IdeState -> IO ()
588- shakeSessionInit ide@ IdeState {.. } = do
589- initSession <- newSession shakeExtras shakeDb [] " shakeSessionInit"
604+ shakeSessionInit :: Recorder Log -> IdeState -> IO ()
605+ shakeSessionInit recorder ide@ IdeState {.. } = do
606+ initSession <- newSession recorder shakeExtras shakeDb [] " shakeSessionInit"
590607 putMVar shakeSession initSession
591608 logDebug (ideLogger ide) " Shake session initialized"
592609
@@ -626,15 +643,19 @@ delayedAction a = do
626643-- | Restart the current 'ShakeSession' with the given system actions.
627644-- Any actions running in the current session will be aborted,
628645-- but actions added via 'shakeEnqueue' will be requeued.
629- shakeRestart :: IdeState -> String -> [DelayedAction () ] -> IO ()
630- shakeRestart IdeState {.. } reason acts =
646+ shakeRestart :: Recorder Log -> IdeState -> String -> [DelayedAction () ] -> IO ()
647+ shakeRestart recorder IdeState {.. } reason acts =
631648 withMVar'
632649 shakeSession
633650 (\ runner -> do
651+ let log = logWith recorder
634652 (stopTime,() ) <- duration (cancelShakeSession runner)
635653 res <- shakeDatabaseProfile shakeDb
636654 backlog <- readTVarIO $ dirtyKeys shakeExtras
637655 queue <- atomicallyNamed " actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
656+
657+ log $ LogBuildSessionRestart reason queue backlog stopTime res
658+
638659 let profile = case res of
639660 Just fp -> " , profile saved at " <> fp
640661 _ -> " "
@@ -643,14 +664,13 @@ shakeRestart IdeState{..} reason acts =
643664 queueMsg = " with queue " ++ show (map actionName queue)
644665 keysMsg = " for keys " ++ show (HSet. toList backlog) ++ " "
645666 abortMsg = " (aborting the previous one took " ++ showDuration stopTime ++ profile ++ " )"
646- logDebug (logger shakeExtras) msg
647667 notifyTestingLogMessage shakeExtras msg
648668 )
649669 -- It is crucial to be masked here, otherwise we can get killed
650670 -- between spawning the new thread and updating shakeSession.
651671 -- See https://github.com/haskell/ghcide/issues/79
652672 (\ () -> do
653- (,() ) <$> newSession shakeExtras shakeDb acts reason)
673+ (,() ) <$> newSession recorder shakeExtras shakeDb acts reason)
654674
655675notifyTestingLogMessage :: ShakeExtras -> T. Text -> IO ()
656676notifyTestingLogMessage extras msg = do
@@ -684,12 +704,13 @@ shakeEnqueue ShakeExtras{actionQueue, logger} act = do
684704-- | Set up a new 'ShakeSession' with a set of initial actions
685705-- Will crash if there is an existing 'ShakeSession' running.
686706newSession
687- :: ShakeExtras
707+ :: Recorder Log
708+ -> ShakeExtras
688709 -> ShakeDatabase
689710 -> [DelayedActionInternal ]
690711 -> String
691712 -> IO ShakeSession
692- newSession extras@ ShakeExtras {.. } shakeDb acts reason = do
713+ newSession recorder extras@ ShakeExtras {.. } shakeDb acts reason = do
693714 IdeOptions {optRunSubset} <- getIdeOptionsIO extras
694715 reenqueued <- atomicallyNamed " actionQueue - peek" $ peekInProgress actionQueue
695716 allPendingKeys <-
@@ -712,7 +733,7 @@ newSession extras@ShakeExtras{..} shakeDb acts reason = do
712733 let msg = T. pack $ " finish: " ++ actionName d
713734 ++ " (took " ++ showDuration runTime ++ " )"
714735 liftIO $ do
715- logPriority logger (actionPriority d) msg
736+ logWith recorder $ LogDelayedAction d runTime
716737 notifyTestingLogMessage extras msg
717738
718739 -- The inferred type signature doesn't work in ghc >= 9.0.1
@@ -729,14 +750,19 @@ newSession extras@ShakeExtras{..} shakeDb acts reason = do
729750 Right _ -> " completed"
730751 let msg = T. pack $ " Finishing build session(" ++ res' ++ " )"
731752 return $ do
732- logDebug logger msg
753+ let exception =
754+ case res of
755+ Left e -> Just e
756+ _ -> Nothing
757+ logWith recorder $ LogBuildSessionFinish exception
733758 notifyTestingLogMessage extras msg
734759
735760 -- Do the work in a background thread
736761 workThread <- asyncWithUnmask workRun
737762
738763 -- run the wrap up in a separate thread since it contains interruptible
739764 -- commands (and we are not using uninterruptible mask)
765+ -- TODO: can possibly swallow exceptions?
740766 _ <- async $ join $ wait workThread
741767
742768 -- Cancelling is required to flush the Shake database when either
0 commit comments