{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances, MultiParamTypeClasses, FlexibleInstances ,FlexibleContexts, UndecidableInstances #-} -- | This module add a 'MonadLoc' instance to the 'Supervisor' monad. This instance generates a trace when a -- uncaugh exception is raised. -- -- . See the MonadLoc package: <http://hackage.haskell.org/package/monadloc> -- -- The package control-monad-exception produces call stacks using @monadloc@, but the @Supervisor@ monad -- produces execution traces thanks to the backtracking mechanism. -- -- The trace is produced after the exception is raised. So it does not generate -- overhead in normal execution. -- -- For more finer control of exceptions, ej. for retrowing exceptions managed outside the Supervisor monad -- , create your own instance -- -- Execute the example at @Demos/TraceExample.hs@ -- -- -- > {-# OPTIONS -F -pgmF MonadLoc #-} -- > module Demos.TraceExample ( -- > -- > ) where -- > -- > import Control.Monad.Loc -- > import Control.Monad.Supervisor.Trace -- > import Control.Monad.Trans -- > -- > main= runTrace $ do -- > liftIO $ print "hello" -- > -- > example -- > -- > example= -- > if True -- > then do -- > liftIO $ print "world" -- > liftIO $ undefined -- > -- > else liftIO $ print "not there" -- -- Produce this trace: -- -- @ -- \"hello\" -- \"world\" -- TraceExample.hs: TRACE (error in the last line): -- . -- main, Demos.TraceExample(Demos\TraceExample.hs): (23, 18) -- main, Demos.TraceExample(Demos\TraceExample.hs): (26, 4) -- example, Demos.TraceExample(Demos\TraceExample.hs): (30, 13) -- example, Demos.TraceExample(Demos\TraceExample.hs): (32, 15) -- exception: Prelude.undefined -- @ -- TO DO: extend it for forward traces and test coverages module Control.Monad.Supervisor.Trace(runTrace) where import Control.Monad.State import Control.Monad.Supervisor import Control.Monad.Loc import Control.Monad.Catch as CMC import Control.Exception (SomeException) import Data.List(intersperse) type Trace= [String] instance (MonadLoc m, Supervise Trace m, MonadCatch m)=> MonadLoc (Sup Trace m) where withLoc loc (Sup f) = Sup $ do withLoc loc $ do r <- f `CMC.catch` handler1 trace <- get case trace of [] -> return r -- all ok trace -> put (loc:trace) >> return r -- is going back with a trace, we add one more line return r where -- detected failure, add the first line of trace with the error, init execution back handler1 (e :: SomeException)= put ["exception: " ++show e] >> return Backward -- | Execute an Supervisor computation and raise an error with a trace when an uncaugh exception -- is raised. It is necessary to preprocess the file with the monadloc-pp preprocessor. -- -- Otherwise, it produces the same error with no trace. runTrace :: Supervise [String] m => Sup [String] m a -> m (Control a) runTrace f= runSup f1 where f1= printBackTrace >> f printBackTrace= do s <- get case s of [] -> breturn() tr -> error (disp tr) where disp tr= "TRACE (error in the last line):\n\n" ++(concat $ intersperse "\n" tr) ---- A less polimorphic version of runTrace. It assumes a state monad for the sole purpose ---- of capturing traces --runTraceState :: Monad m => Sup (StateT [String] m) a -> m (Control a) --runTraceState f= evalStateT (runTrace f) []