\begin{code}
\end{code}
The file is part of the Haskell Object Observation Debugger,
(HOOD) March 2010 release.
HOOD is a small post-mortem debugger for the lazy functional
language Haskell. It is based on the concept of observation of
intermediate data structures, rather than the more traditional
stepping and variable examination paradigm used by imperative
language debuggers.
Copyright (c) Andy Gill, 1992-2000
Copyright (c) The University of Kansas 2010
Copyright (c) Maarten Faddegon, 2013-2015
All rights reserved. HOOD is distributed as free software under
the license in the file "License", which available from the HOOD
web page, http://www.haskell.org/hood
This module produces CDS's, based on the observation made on Haskell
objects, including base types, constructors and functions.
WARNING: unrestricted use of unsafePerformIO below.
This was ported for the version found on www.haskell.org/hood.
%************************************************************************
%* *
\subsection{Exports}
%* *
%************************************************************************
\begin{code}
module Debug.Hoed.Observe
where
\end{code}
%************************************************************************
%* *
\subsection{Imports and infixing}
%* *
%************************************************************************
\begin{code}
import Prelude hiding (Right)
import qualified Prelude
import Control.Monad
import Data.Array as Array
import Debug.Hoed.Fields
import GHC.Generics
import Data.IORef
import System.IO.Unsafe
\end{code}
\begin{code}
import qualified Control.Exception as Exception
import Control.Exception (throw, SomeException(..))
import Data.Dynamic ( Dynamic )
\end{code}
\begin{code}
infixl 9 <<
\end{code}
%************************************************************************
%* *
\subsection{GDM Generics}
%* *
%************************************************************************
The generic implementation of the observer function.
\begin{code}
class Observable a where
observer :: a -> Parent -> a
default observer :: (Generic a, GObservable (Rep a)) => a -> Parent -> a
observer x c = to (gdmobserver (from x) c)
constrain :: a -> a -> a
default constrain :: (Generic a, GConstrain (Rep a)) => a -> a -> a
constrain x c = to (gconstrain (from x) (from c))
class GObservable f where
gdmobserver :: f a -> Parent -> f a
gdmObserveArgs :: f a -> ObserverM (f a)
gdmShallowShow :: f a -> String
constrainBase :: (Show a, Eq a) => a -> a -> a
constrainBase x c | x == c = x
| otherwise = error $ show x ++ " constrained by " ++ show c
\end{code}
A type generic definition of constrain
\begin{code}
class GConstrain f where gconstrain :: f a -> f a -> f a
instance (GConstrain a, GConstrain b) => GConstrain (a :+: b) where
gconstrain (L1 x) (L1 c) = L1 (gconstrain x c)
gconstrain (R1 x) (R1 c) = R1 (gconstrain x c)
instance (GConstrain a, GConstrain b) => GConstrain (a :*: b) where
gconstrain (x :*: y) (c :*: d) = (gconstrain x c) :*: (gconstrain y d)
instance GConstrain U1 where
gconstrain x c = x
instance (Observable a) => GConstrain (K1 i a) where
gconstrain (K1 x) (K1 c) = K1 (constrain x c)
instance (GConstrain a) => GConstrain (M1 D d a) where
gconstrain (M1 x) (M1 c) = M1 (gconstrain x c)
instance (GConstrain a, Selector s) => GConstrain (M1 S s a) where
gconstrain m@(M1 x) n@(M1 c) | selName m == selName n = M1 (gconstrain x c)
instance (GConstrain a, Constructor c) => GConstrain (M1 C c a) where
gconstrain m@(M1 x) n@(M1 c) | conName m == conName n = M1 (gconstrain x c)
\end{code}
Observing the children of Data types of kind *.
\begin{code}
instance (FieldLimit ('S ('S ('S ('S ('S ('S 'Z)))))) a, GObservable a) => GObservable (M1 D d a) where
gdmobserver m@(M1 x) cxt = M1 (gdmobserver x cxt)
gdmObserveArgs = gthunk
gdmShallowShow = error "gdmShallowShow not defined on the <<data meta type>>"
instance (GObservable a, Selector s) => GObservable (M1 S s a) where
gdmobserver (M1 x) cxt = M1 (gdmobserver x cxt)
gdmObserveArgs = gthunk
gdmShallowShow = error "gdmShallowShow not defined on the <<selector meta type>>"
instance (GObservable a, Constructor c) => GObservable (M1 C c a) where
gdmobserver m1 = send (gdmShallowShow m1) (gdmObserveArgs m1)
gdmObserveArgs (M1 x) = do {x' <- gdmObserveArgs x; return (M1 x')}
gdmShallowShow = conName
instance GObservable U1 where
gdmobserver x _ = x
gdmObserveArgs = return
gdmShallowShow = error "gdmShallowShow not defined on <<the unit type>>"
instance (GObservable a, GObservable b) => GObservable (a :+: b) where
gdmobserver (L1 x) = send (gdmShallowShow x) (gdmObserveArgs $ L1 x)
gdmobserver (R1 x) = send (gdmShallowShow x) (gdmObserveArgs $ R1 x)
gdmShallowShow (L1 x) = gdmShallowShow x
gdmShallowShow (R1 x) = gdmShallowShow x
gdmObserveArgs (L1 x) = do {x' <- gdmObserveArgs x; return (L1 x')}
gdmObserveArgs (R1 x) = do {x' <- gdmObserveArgs x; return (R1 x')}
instance (GObservable a, GObservable b) => GObservable (a :*: b) where
gdmobserver (a :*: b) cxt = (gdmobserver a cxt) :*: (gdmobserver b cxt)
gdmObserveArgs (a :*: b) = do
a' <- gdmObserveArgs a
b' <- gdmObserveArgs b
return (a' :*: b')
gdmShallowShow = error "gdmShallowShow not defined on <<the product type>>"
instance (Observable a) => GObservable (K1 i a) where
gdmobserver (K1 x) cxt = K1 $ observer x cxt
gdmObserveArgs = gthunk
gdmShallowShow = error "gdmShallowShow not defined on <<the constant type>>"
\end{code}
Observing functions is done via the ad-hoc mechanism, because
we provide an instance definition the default is ignored for
this type.
\begin{code}
instance (Observable a,Observable b) => Observable (a -> b) where
observer fn cxt arg = gdmFunObserver cxt fn arg
constrain = error "how to constrain the function type?"
\end{code}
Observing the children of Data types of kind *->*.
\begin{code}
gdmFunObserver :: (Observable a,Observable b) => Parent -> (a->b) -> (a->b)
gdmFunObserver cxt fn arg
= sendObserveFnPacket
(do arg' <- thunk observer arg
thunk observer (fn arg')
) cxt
\end{code}
%************************************************************************
%* *
\subsection{Instances}
%* *
%************************************************************************
The Haskell Base types
\begin{code}
instance Observable Int where observer = observeBase
constrain = constrainBase
instance Observable Bool where observer = observeBase
constrain = constrainBase
instance Observable Integer where observer = observeBase
constrain = constrainBase
instance Observable Float where observer = observeBase
constrain = constrainBase
instance Observable Double where observer = observeBase
constrain = constrainBase
instance Observable Char where observer = observeBase
constrain = constrainBase
instance Observable () where observer = observeOpaque "()"
constrain = constrainBase
observeBase :: (Show a) => a -> Parent -> a
observeBase lit cxt = seq lit $ send (show lit) (return lit) cxt
observeOpaque :: String -> a -> Parent -> a
observeOpaque str val cxt = seq val $ send str (return val) cxt
\end{code}
The Constructors.
\begin{code}
instance (Observable a,Observable b) => Observable (a,b) where
observer (a,b) = send "," (return (,) << a << b)
instance (Observable a,Observable b,Observable c) => Observable (a,b,c) where
observer (a,b,c) = send "," (return (,,) << a << b << c)
instance (Observable a,Observable b,Observable c,Observable d)
=> Observable (a,b,c,d) where
observer (a,b,c,d) = send "," (return (,,,) << a << b << c << d)
instance (Observable a,Observable b,Observable c,Observable d,Observable e)
=> Observable (a,b,c,d,e) where
observer (a,b,c,d,e) = send "," (return (,,,,) << a << b << c << d << e)
instance (Observable a) => Observable [a] where
observer (a:as) = send ":" (return (:) << a << as)
observer [] = send "[]" (return [])
instance (Observable a) => Observable (Maybe a) where
observer (Just a) = send "Just" (return Just << a)
observer Nothing = send "Nothing" (return Nothing)
instance (Observable a,Observable b) => Observable (Either a b) where
observer (Left a) = send "Left" (return Left << a)
observer (Prelude.Right a) = send "Right" (return Prelude.Right << a)
\end{code}
Arrays.
\begin{code}
instance (Ix a,Observable a,Observable b) => Observable (Array.Array a b) where
observer arr = send "array" (return Array.array << Array.bounds arr
<< Array.assocs arr
)
constrain = undefined
\end{code}
IO monad.
\begin{code}
instance (Observable a) => Observable (IO a) where
observer fn cxt =
do res <- fn
send "<IO>" (return return << res) cxt
constrain = undefined
\end{code}
The Exception *datatype* (not exceptions themselves!).
\begin{code}
instance Observable SomeException where
observer e = send ("<Exception> " ++ show e) (return e)
constrain = undefined
instance Observable Dynamic where
observer = observeOpaque "<Dynamic>"
constrain = undefined
\end{code}
%************************************************************************
%* *
\subsection{Classes and Data Definitions}
%* *
%************************************************************************
MF: why/when do we need these types?
\begin{code}
type Observing a = a -> a
newtype Observer = O (forall a . (Observable a) => String -> a -> a)
\end{code}
%************************************************************************
%* *
\subsection{The ObserveM Monad}
%* *
%************************************************************************
The Observer monad, a simple state monad,
for placing numbers on sub-observations.
\begin{code}
newtype ObserverM a = ObserverM { runMO :: Int -> Int -> (a,Int) }
instance Functor ObserverM where
fmap = liftM
#if __GLASGOW_HASKELL__ >= 710
instance Applicative ObserverM where
pure = return
(<*>) = ap
#endif
instance Monad ObserverM where
return a = ObserverM (\ c i -> (a,i))
fn >>= k = ObserverM (\ c i ->
case runMO fn c i of
(r,i2) -> runMO (k r) c i2
)
thunk :: (a -> Parent -> a) -> a -> ObserverM a
thunk f a = ObserverM $ \ parent port ->
( observer_ f a (Parent
{ parentUID = parent
, parentPosition = port
})
, port+1 )
gthunk :: (GObservable f) => f a -> ObserverM (f a)
gthunk a = ObserverM $ \ parent port ->
( gdmobserver_ a (Parent
{ parentUID = parent
, parentPosition = port
})
, port+1 )
(<<) :: (Observable a) => ObserverM (a -> b) -> a -> ObserverM b
fn << a = gdMapM (thunk observer) fn a
gdMapM :: (Monad m)
=> (a -> m a)
-> m (a -> b)
-> a
-> m b
gdMapM f c a = do { c' <- c ; a' <- f a ; return (c' a') }
\end{code}
%************************************************************************
%* *
\subsection{observe and friends}
%* *
%************************************************************************
Our principle function and class
\begin{code}
gobserve :: (a->Parent->a) -> String -> a -> (a,Int)
gobserve f name a = generateContext f name a
observe :: (Observable a) => String -> a -> a
observe lbl = fst . (gobserve observer lbl)
observer_ :: (a -> Parent -> a) -> a -> Parent -> a
observer_ f a context = sendEnterPacket f a context
gdmobserver_ :: (GObservable f) => f a -> Parent -> f a
gdmobserver_ a context = gsendEnterPacket a context
\end{code}
The functions that output the data. All are dirty.
\begin{code}
unsafeWithUniq :: (Int -> IO a) -> a
unsafeWithUniq fn
= unsafePerformIO $ do { node <- getUniq
; fn node
}
\end{code}
\begin{code}
generateContext :: (a->Parent->a) -> String -> a -> (a,Int)
generateContext f label orig = unsafeWithUniq $ \node ->
do sendEvent node (Parent 0 0) (Observe label)
return (observer_ f orig (Parent
{ parentUID = node
, parentPosition = 0
})
, node)
send :: String -> ObserverM a -> Parent -> a
send consLabel fn context = unsafeWithUniq $ \ node ->
do { let (r,portCount) = runMO fn node 0
; sendEvent node context (Cons portCount consLabel)
; return r
}
sendEnterPacket :: (a -> Parent -> a) -> a -> Parent -> a
sendEnterPacket f r context = unsafeWithUniq $ \ node ->
do { sendEvent node context Enter
; ourCatchAllIO (evaluate (f r context))
(handleExc context)
}
gsendEnterPacket :: (GObservable f) => f a -> Parent -> f a
gsendEnterPacket r context = unsafeWithUniq $ \ node ->
do { sendEvent node context Enter
; ourCatchAllIO (evaluate (gdmobserver r context))
(handleExc context)
}
evaluate :: a -> IO a
evaluate a = a `seq` return a
sendObserveFnPacket :: ObserverM a -> Parent -> a
sendObserveFnPacket fn context
= unsafeWithUniq $ \ node ->
do { let (r,_) = runMO fn node 0
; sendEvent node context Fun
; return r
}
\end{code}
%************************************************************************
%* *
\subsection{Event stream}
%* *
%************************************************************************
Trival output functions
\begin{code}
type Trace = [Event]
data Event = Event
{ eventUID :: !UID
, eventParent :: !Parent
, change :: !Change
}
deriving (Eq,Generic)
data Change
= Observe !String
| Cons !Int !String
| Enter
| Fun
deriving (Eq, Show,Generic)
type ParentPosition = Int
data Parent = Parent
{ parentUID :: !UID
, parentPosition :: !ParentPosition
} deriving (Eq,Generic)
instance Show Event where
show e = (show . eventUID $ e) ++ ": " ++ (show . change $ e) ++ " (" ++ (show . eventParent $ e) ++ ")"
instance Show Parent where
show p = "P " ++ (show . parentUID $ p) ++ " " ++ (show . parentPosition $ p)
root = Parent 0 0
isRootEvent :: Event -> Bool
isRootEvent e = case change e of Observe{} -> True; _ -> False
startEventStream :: IO ()
startEventStream = writeIORef events []
endEventStream :: IO Trace
endEventStream =
do { es <- readIORef events
; writeIORef events badEvents
; return es
}
sendEvent :: Int -> Parent -> Change -> IO ()
sendEvent nodeId parent change =
do { let !event = Event nodeId parent change
; atomicModifyIORef' events (\es -> (event : es, ()))
}
events :: IORef Trace
events = unsafePerformIO $ newIORef badEvents
badEvents :: Trace
badEvents = error "Bad Event Stream"
\end{code}
%************************************************************************
%* *
\subsection{unique name supply code}
%* *
%************************************************************************
Use the single threaded version
\begin{code}
type UID = Int
initUniq :: IO ()
initUniq = writeIORef uniq 1
getUniq :: IO UID
getUniq = atomicModifyIORef' uniq (\n -> (n+1,n))
peepUniq :: IO UID
peepUniq = readIORef uniq
uniq :: IORef UID
uniq = unsafePerformIO $ newIORef 1
\end{code}
%************************************************************************
%* *
\subsection{Global, initualizers, etc}
%* *
%************************************************************************
-- \begin{code}
-- openObserveGlobal :: IO ()
-- openObserveGlobal =
-- do { initUniq
-- ; startEventStream
-- }
--
-- closeObserveGlobal :: IO Trace
-- closeObserveGlobal =
-- do { evs <- endEventStream
-- ; putStrLn ""
-- ; return evs
-- }
-- \end{code}
%************************************************************************
%* *
\subsection{Simulations}
%* *
%************************************************************************
Here we provide stubs for the functionally that is not supported
by some compilers, and provide some combinators of various flavors.
\begin{code}
ourCatchAllIO :: IO a -> (SomeException -> IO a) -> IO a
ourCatchAllIO = Exception.catch
handleExc :: Parent -> SomeException -> IO a
handleExc context exc = return (send (show exc) (return (throw exc)) context)
\end{code}
%************************************************************************