hood 0.2.1 → 0.3
raw patch · 4 files changed
+879/−912 lines, 4 filesdep +FPrettydep +ghc-primdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: FPretty, ghc-prim
Dependency ranges changed: base
API changes (from Hackage documentation)
- Debug.Hood.Observe: instance (Ix a, Observable a, Observable b) => Observable (Array a b)
- Debug.Hood.Observe: instance (Observable a, Observable b) => Observable (Either a b)
- Debug.Hood.Observe: instance (Observable a, Observable b) => Observable (a -> b)
- Debug.Hood.Observe: instance (Observable a, Observable b) => Observable (a, b)
- Debug.Hood.Observe: instance (Observable a, Observable b, Observable c) => Observable (a, b, c)
- Debug.Hood.Observe: instance (Observable a, Observable b, Observable c, Observable d) => Observable (a, b, c, d)
- Debug.Hood.Observe: instance (Observable a, Observable b, Observable c, Observable d, Observable e) => Observable (a, b, c, d, e)
- Debug.Hood.Observe: instance Applicative ObserverM
- Debug.Hood.Observe: instance Eq CDS
- Debug.Hood.Observe: instance Eq DOC
- Debug.Hood.Observe: instance Eq Doc
- Debug.Hood.Observe: instance Eq Output
- Debug.Hood.Observe: instance Functor ObserverM
- Debug.Hood.Observe: instance Monad ObserverM
- Debug.Hood.Observe: instance Observable ()
- Debug.Hood.Observe: instance Observable Bool
- Debug.Hood.Observe: instance Observable Char
- Debug.Hood.Observe: instance Observable Double
- Debug.Hood.Observe: instance Observable Dynamic
- Debug.Hood.Observe: instance Observable Float
- Debug.Hood.Observe: instance Observable Int
- Debug.Hood.Observe: instance Observable Integer
- Debug.Hood.Observe: instance Observable SomeException
- Debug.Hood.Observe: instance Observable a => Observable (IO a)
- Debug.Hood.Observe: instance Observable a => Observable (Maybe a)
- Debug.Hood.Observe: instance Observable a => Observable [a]
- Debug.Hood.Observe: instance Ord CDS
- Debug.Hood.Observe: instance Ord Output
- Debug.Hood.Observe: instance Show CDS
- Debug.Hood.Observe: instance Show Change
- Debug.Hood.Observe: instance Show DOC
- Debug.Hood.Observe: instance Show Doc
- Debug.Hood.Observe: instance Show Event
- Debug.Hood.Observe: instance Show Parent
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.GObservable a, Debug.Hood.Observe.GObservable b) => Debug.Hood.Observe.GObservable (a GHC.Generics.:*: b)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.GObservable a, Debug.Hood.Observe.GObservable b) => Debug.Hood.Observe.GObservable (a GHC.Generics.:+: b)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.GObservable a, GHC.Generics.Constructor c) => Debug.Hood.Observe.GObservable (GHC.Generics.M1 GHC.Generics.C c a)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.GObservable a, GHC.Generics.Selector s) => Debug.Hood.Observe.GObservable (GHC.Generics.M1 GHC.Generics.S s a)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b) => Debug.Hood.Observe.Observable (Data.Either.Either a b)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b) => Debug.Hood.Observe.Observable (a -> b)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b) => Debug.Hood.Observe.Observable (a, b)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b, Debug.Hood.Observe.Observable c) => Debug.Hood.Observe.Observable (a, b, c)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b, Debug.Hood.Observe.Observable c, Debug.Hood.Observe.Observable d) => Debug.Hood.Observe.Observable (a, b, c, d)
+ Debug.Hood.Observe: instance (Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b, Debug.Hood.Observe.Observable c, Debug.Hood.Observe.Observable d, Debug.Hood.Observe.Observable e) => Debug.Hood.Observe.Observable (a, b, c, d, e)
+ Debug.Hood.Observe: instance (GHC.Arr.Ix a, Debug.Hood.Observe.Observable a, Debug.Hood.Observe.Observable b) => Debug.Hood.Observe.Observable (GHC.Arr.Array a b)
+ Debug.Hood.Observe: instance Debug.Hood.Observe.GObservable GHC.Generics.U1
+ Debug.Hood.Observe: instance Debug.Hood.Observe.GObservable a => Debug.Hood.Observe.GObservable (GHC.Generics.M1 GHC.Generics.D d a)
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable ()
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable Data.Dynamic.Dynamic
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Exception.SomeException
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Integer.Type.Integer
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Types.Bool
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Types.Char
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Types.Double
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Types.Float
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable GHC.Types.Int
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable a => Debug.Hood.Observe.GObservable (GHC.Generics.K1 i a)
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable a => Debug.Hood.Observe.Observable (GHC.Base.Maybe a)
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable a => Debug.Hood.Observe.Observable (GHC.Types.IO a)
+ Debug.Hood.Observe: instance Debug.Hood.Observe.Observable a => Debug.Hood.Observe.Observable [a]
+ Debug.Hood.Observe: instance GHC.Base.Applicative Debug.Hood.Observe.ObserverM
+ Debug.Hood.Observe: instance GHC.Base.Functor Debug.Hood.Observe.ObserverM
+ Debug.Hood.Observe: instance GHC.Base.Monad Debug.Hood.Observe.ObserverM
+ Debug.Hood.Observe: instance GHC.Classes.Eq Debug.Hood.Observe.CDS
+ Debug.Hood.Observe: instance GHC.Classes.Eq Debug.Hood.Observe.Output
+ Debug.Hood.Observe: instance GHC.Classes.Ord Debug.Hood.Observe.CDS
+ Debug.Hood.Observe: instance GHC.Classes.Ord Debug.Hood.Observe.Output
+ Debug.Hood.Observe: instance GHC.Show.Show Debug.Hood.Observe.CDS
+ Debug.Hood.Observe: instance GHC.Show.Show Debug.Hood.Observe.Change
+ Debug.Hood.Observe: instance GHC.Show.Show Debug.Hood.Observe.Event
+ Debug.Hood.Observe: instance GHC.Show.Show Debug.Hood.Observe.Parent
- Debug.Hood.Observe: (<<) :: Observable a => ObserverM (a -> b) -> a -> ObserverM b
+ Debug.Hood.Observe: (<<) :: (Observable a) => ObserverM (a -> b) -> a -> ObserverM b
- Debug.Hood.Observe: O :: (forall a. Observable a => String -> a -> a) -> Observer
+ Debug.Hood.Observe: O :: (forall a. (Observable a) => String -> a -> a) -> Observer
- Debug.Hood.Observe: class Observable a where observers label arg = defaultObservers label arg
+ Debug.Hood.Observe: class Observable a where observer x c = to (gdmobserver (from x) c) observers label arg = defaultObservers label arg
- Debug.Hood.Observe: observe :: Observable a => String -> a -> a
+ Debug.Hood.Observe: observe :: (Observable a) => String -> a -> a
- Debug.Hood.Observe: observeBase :: Show a => a -> Parent -> a
+ Debug.Hood.Observe: observeBase :: (Show a) => a -> Parent -> a
- Debug.Hood.Observe: printO :: Show a => a -> IO ()
+ Debug.Hood.Observe: printO :: (Show a) => a -> IO ()
- Debug.Hood.Observe: thunk :: Observable a => a -> ObserverM a
+ Debug.Hood.Observe: thunk :: (Observable a) => a -> ObserverM a
Files
- CHANGELOG.md +6/−0
- Debug/Hood/Observe.hs +865/−0
- Debug/Hood/Observe.lhs +0/−908
- hood.cabal +8/−4
CHANGELOG.md view
@@ -1,2 +1,8 @@+## 0.3+* `Observable` instances can be derived for `Generic` instances using+ `DefaultSignatures` ([MaartenFaddegon](https://github.com/ku-fpg/hood/pull/1))+* Depend on the `FPretty` library for pretty-printing, which requires+ `base-4.5` or greater.+ ## 0.2.1 * Fixed build with GHC 7.10
+ Debug/Hood/Observe.hs view
@@ -0,0 +1,865 @@+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeOperators #-}++{-+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>.+-}+module Debug.Hood.Observe++{-+************************************************************************+* *+ Exports+* *+************************************************************************+-}+ (+ -- * The main Hood API+++ observe -- (Observable a) => String -> a -> a+ , Observer(..) -- contains a 'forall' typed observe (if supported).+ , Observing -- a -> a+ , Observable(..) -- Class+ , runO -- IO a -> IO ()+ , printO -- a -> IO ()+ , putStrO -- String -> IO ()++ -- * For advanced users, that want to render their own datatypes.+ , (<<) -- (Observable a) => ObserverM (a -> b) -> a -> ObserverM b+ , thunk -- (Observable a) => a -> ObserverM a+ , send+ , observeBase+ , observeOpaque++ -- * For users that want to write there own render drivers.++ , debugO -- IO a -> IO [CDS]+ , CDS(..)+ ) where++{-+************************************************************************+* *+ Imports and infixing+* *+************************************************************************+-}+import System.IO+import Data.Maybe+import Control.Applicative+import Control.Monad+import Data.Array as Array+import Data.List+--import System++-- The only non standard one we assume+--import IOExts+import Data.IORef+import System.IO.Unsafe+import GHC.Generics+import Text.PrettyPrint.FPretty hiding ((<$>))++import Control.Concurrent++import Control.Exception ( Exception, throw )+import qualified Control.Exception as Exception+{-+ ( catch+ , Exception(..)+ , throw+ ) as Exception+-}+import Data.Dynamic ( Dynamic )++import Prelude++infixl 9 <<++{-+************************************************************************+* *+ External start functions+* *+************************************************************************++Run the observe ridden code.+-}++-- | run some code and return the CDS structure (for when you want to write your own debugger).+debugO :: IO a -> IO [CDS]+debugO program =+ do { initUniq+ ; startEventStream+ ; let errorMsg e = "[Escaping Exception in Code : " ++ show e ++ "]"+ ; ourCatchAllIO (do { program ; return () })+ (hPutStrLn stderr . errorMsg)+ ; events <- endEventStream+ ; return (eventsToCDS events)+ }++-- | print a value, with debugging+printO :: (Show a) => a -> IO ()+printO expr = runO (print expr)++-- | print a string, with debugging+putStrO :: String -> IO ()+putStrO expr = runO (putStr expr)++-- | The main entry point; run some IO code, and debug inside it.+--+-- An example of using this debugger is+--+-- @runO (print [ observe "+1" (+1) x | x <- observe "xs" [1..3]])@+--+-- @[2,3,4]+-- -- +1+-- { \ 1 -> 2+-- }+-- -- +1+-- { \ 2 -> 3+-- }+-- -- +1+-- { \ 3 -> 4+-- }+-- -- xs+-- 1 : 2 : 3 : []@+--+-- Which says, the return is @[2,3,4]@, there were @3@ calls to +1+-- (showing arguments and results), and @xs@, which was the list+-- @1 : 2 : 3 : []@.+--++runO :: IO a -> IO ()+runO program =+ do { cdss <- debugO program+ ; let cdss1 = rmEntrySet cdss+ ; let cdss2 = simplifyCDSSet cdss1+ ; let output1 = cdssToOutput cdss2+ ; let output2 = commonOutput output1+ ; let ptyout = pretty 80 (foldr (<>) nil (map renderTop output2))+ ; hPutStrLn stderr ""+ ; hPutStrLn stderr ptyout+ }++{-+************************************************************************+* *+ Simulations+* *+************************************************************************++Here we provide stubs for the functionally that is not supported+by some compilers, and provide some combinators of various flavors.+-}+++ourCatchAllIO :: IO a -> (Exception.SomeException -> IO a) -> IO a+ourCatchAllIO = Exception.catch++handleExc :: Parent -> Exception.SomeException -> IO a+handleExc context exc = return (send "throw" (return throw << exc) context)++{-+************************************************************************+* *+ Instances+* *+************************************************************************++The Haskell Base types+-}++instance Observable Int where { observer = observeBase }+instance Observable Bool where { observer = observeBase }+instance Observable Integer where { observer = observeBase }+instance Observable Float where { observer = observeBase }+instance Observable Double where { observer = observeBase }+instance Observable Char where { observer = observeBase }++instance Observable () where { observer = observeOpaque "()" }++-- utilities for base types.+-- The strictness (by using seq) is the same+-- as the pattern matching done on other constructors.+-- we evalute to WHNF, and not further.++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++-- The Constructors.++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 (Right a) = send "Right" (return Right << a)++-- Arrays.++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+ )++-- IO monad.++instance (Observable a) => Observable (IO a) where+ observer fn cxt =+ do res <- fn+ send "<IO>" (return return << res) cxt+++-- Functions.++instance (Observable a,Observable b) => Observable (a -> b) where+ observer fn cxt arg = sendObserveFnPacket (+ do arg <- thunk arg+ thunk (fn arg)) cxt++ observers = defaultFnObservers++-- The Exception *datatype* (not exceptions themselves!).+-- For now, we only display IOExceptions and calls to Error.++instance Observable Exception.SomeException where+-- observer (IOException a) = observeOpaque "IOException" (IOException a)+-- observer (ErrorCall a) = send "ErrorCall" (return ErrorCall << a)+ observer other = send "<Exception>" (return other)++instance Observable Dynamic where { observer = observeOpaque "<Dynamic>" }++{-+************************************************************************+* *+ Classes and Data Definition+* *+************************************************************************+-}++-- | The 'Observable' class defines how data types are observed.+-- For 'Generic' data types, this can be derived. For example:+--+-- @+-- data MyType = MyConstr Int String deriving 'Generic'+-- instance 'Observable' MyType+-- @+class Observable a where+ {-+ - This reveals the name of a specific constructor.+ - and gets ready to explain the sub-components.+ -+ - We put the context second so we can do eta-reduction+ - with some of our definitions.+ -}+ observer :: a -> Parent -> a+ default observer :: (Generic a, GObservable (Rep a)) => a -> Parent -> a+ observer x c = to (gdmobserver (from x) c)++ {-+ - This used used to group several observer instances together.+ -}+ observers :: String -> (Observer -> a) -> a+ observers label arg = defaultObservers label arg++class GObservable f where+ gdmobserver :: f a -> Parent -> f a+ gdmObserveChildren :: f a -> ObserverM (f a)+ gdmShallowShow :: f a -> String++-- Meta: data types+instance (GObservable a) => GObservable (M1 D d a) where+ gdmobserver m@(M1 x) cxt = M1 (gdmobserver x cxt)+ gdmObserveChildren = gthunk+ gdmShallowShow = error "gdmShallowShow not defined on <<Meta: data types>>"++-- Meta: Selectors+instance (GObservable a, Selector s) => GObservable (M1 S s a) where+ gdmobserver m@(M1 x) cxt+ = M1 (gdmobserver x cxt)+ -- Uncomment next two lines to record selector names+ -- selName m == "" = M1 (gdmobserver x cxt)+ -- otherwise = M1 (send (selName m ++ " =") (gdmObserveChildren x) cxt)+ gdmObserveChildren = gthunk+ gdmShallowShow = error "gdmShallowShow not defined on <<Meta: selectors>>"++-- Meta: Constructors+instance (GObservable a, Constructor c) => GObservable (M1 C c a) where+ gdmobserver m1 = send (gdmShallowShow m1) (gdmObserveChildren m1)+ gdmObserveChildren (M1 x) = do {x' <- gdmObserveChildren x; return (M1 x')}+ gdmShallowShow = conName++-- Unit: used for constructors without arguments+instance GObservable U1 where+ gdmobserver x _ = x+ gdmObserveChildren = return+ gdmShallowShow = error "gdmShallowShow not defined on <<the unit type>>"++-- Sums: encode choice between constructors+instance (GObservable a, GObservable b) => GObservable (a :+: b) where+ gdmobserver (L1 x) = send (gdmShallowShow x) (gdmObserveChildren $ L1 x)+ gdmobserver (R1 x) = send (gdmShallowShow x) (gdmObserveChildren $ R1 x)+ gdmShallowShow (L1 x) = gdmShallowShow x+ gdmShallowShow (R1 x) = gdmShallowShow x+ gdmObserveChildren (L1 x) = do {x' <- gdmObserveChildren x; return (L1 x')}+ gdmObserveChildren (R1 x) = do {x' <- gdmObserveChildren x; return (R1 x')}++-- Products: encode multiple arguments to constructors+instance (GObservable a, GObservable b) => GObservable (a :*: b) where+ gdmobserver (a :*: b) cxt = (gdmobserver a cxt) :*: (gdmobserver b cxt)+ gdmObserveChildren (a :*: b) = do a' <- gdmObserveChildren a+ b' <- gdmObserveChildren b+ return (a' :*: b')+ gdmShallowShow = error "gdmShallowShow not defined on <<the product type>>"++-- Constants: additional parameters and recursion of kind *+instance (Observable a) => GObservable (K1 i a) where+ gdmobserver (K1 x) cxt = K1 $ observer x cxt+ gdmObserveChildren = gthunk+ gdmShallowShow = error "gdmShallowShow not defined on <<constant types>>"++gthunk :: (GObservable f) => f a -> ObserverM (f a)+gthunk a = ObserverM $ \ parent port ->+ ( gdmobserver_ a (Parent+ { observeParent = parent+ , observePort = port+ })+ , port+1 )++gdmobserver_ :: (GObservable f) => f a -> Parent -> f a+gdmobserver_ a context = gsendEnterPacket a 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)+ }++type Observing a = a -> a++newtype Observer = O (forall a . (Observable a) => String -> a -> a)++defaultObservers :: (Observable a) => String -> (Observer -> a) -> a+defaultObservers label fn = unsafeWithUniq $ \ node ->+ do { sendEvent node (Parent 0 0) (Observe label)+ ; let observe' sublabel a+ = unsafeWithUniq $ \ subnode ->+ do { sendEvent subnode (Parent node 0)+ (Observe sublabel)+ ; return (observer_ a (Parent+ { observeParent = subnode+ , observePort = 0+ }))+ }+ ; return (observer_ (fn (O observe'))+ (Parent+ { observeParent = node+ , observePort = 0+ }))+ }+defaultFnObservers :: (Observable a, Observable b)+ => String -> (Observer -> a -> b) -> a -> b+defaultFnObservers label fn arg = unsafeWithUniq $ \ node ->+ do { sendEvent node (Parent 0 0) (Observe label)+ ; let observe' sublabel a+ = unsafeWithUniq $ \ subnode ->+ do { sendEvent subnode (Parent node 0)+ (Observe sublabel)+ ; return (observer_ a (Parent+ { observeParent = subnode+ , observePort = 0+ }))+ }+ ; return (observer_ (fn (O observe'))+ (Parent+ { observeParent = node+ , observePort = 0+ }) arg)+ }++{-+************************************************************************+* *+ The ObserveM Monad+* *+************************************************************************++The Observer monad, a simple state monad,+for placing numbers on sub-observations.+-}++newtype ObserverM a = ObserverM { runMO :: Int -> Int -> (a,Int) }++instance Functor ObserverM where+ fmap = liftM++instance Applicative ObserverM where+ pure = return+ (<*>) = ap++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 :: (Observable a) => a -> ObserverM a+thunk a = ObserverM $ \ parent port ->+ ( observer_ a (Parent+ { observeParent = parent+ , observePort = port+ })+ , port+1 )++(<<) :: (Observable a) => ObserverM (a -> b) -> a -> ObserverM b+fn << a = do { fn' <- fn ; a' <- thunk a ; return (fn' a') }++{-+************************************************************************+* *+ observe and friends+* *+************************************************************************++Our principal function and class+-}++-- | 'observe' observes data structures in flight.+--+-- An example of use is+-- @+-- map (+1) . observe \"intermeduate\" . map (+2)+-- @+--+-- In this example, we observe the value that flows from the producer+-- @map (+2)@ to the consumer @map (+1)@.+--+-- 'observe' can also observe functions as well a structural values.+--+{-# NOINLINE observe #-}+observe :: (Observable a) => String -> a -> a+observe name a = generateContext name a++{- This gets called before observer, allowing us to mark+ - we are entering a, before we do case analysis on+ - our object.+ -}++{-# NOINLINE observer_ #-}+observer_ :: (Observable a) => a -> Parent -> a+observer_ a context = sendEnterPacket a context++data Parent = Parent+ { observeParent :: !Int -- my parent+ , observePort :: !Int -- my branch number+ } deriving Show+root = Parent 0 0+++-- The functions that output the data. All are dirty.++unsafeWithUniq :: (Int -> IO a) -> a+unsafeWithUniq fn+ = unsafePerformIO $ do { node <- getUniq+ ; fn node+ }++generateContext :: (Observable a) => String -> a -> a+generateContext label orig = unsafeWithUniq $ \ node ->+ do { sendEvent node (Parent 0 0) (Observe label)+ ; return (observer_ orig (Parent+ { observeParent = node+ , observePort = 0+ })+ )+ }++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 :: (Observable a) => a -> Parent -> a+sendEnterPacket r context = unsafeWithUniq $ \ node ->+ do { sendEvent node context Enter+ ; ourCatchAllIO (evaluate (observer 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+ }++{-+************************************************************************+* *+ Event stream+* *+************************************************************************++Trival output functions+-}++data Event = Event+ { portId :: !Int+ , parent :: !Parent+ , change :: !Change+ }+ deriving Show++data Change+ = Observe !String+ | Cons !Int !String+ | Enter+ | Fun+ deriving Show++startEventStream :: IO ()+startEventStream = writeIORef events []++endEventStream :: IO [Event]+endEventStream =+ do { es <- readIORef events+ ; writeIORef events badEvents+ ; return es+ }++sendEvent :: Int -> Parent -> Change -> IO ()+sendEvent nodeId parent change =+ do { nodeId `seq` parent `seq` return ()+ ; change `seq` return ()+ ; takeMVar sendSem+ ; es <- readIORef events+ ; let event = Event nodeId parent change+ ; writeIORef events (event `seq` (event : es))+ ; putMVar sendSem ()+ }++-- local+events :: IORef [Event]+events = unsafePerformIO $ newIORef badEvents++badEvents :: [Event]+badEvents = error "Bad Event Stream"++-- use as a trivial semiphore+{-# NOINLINE sendSem #-}+sendSem :: MVar ()+sendSem = unsafePerformIO $ newMVar ()+-- end local++{-+************************************************************************+* *+ Unique name supply code+* *+************************************************************************++Use the single threaded version+-}++initUniq :: IO ()+initUniq = writeIORef uniq 1++getUniq :: IO Int+getUniq+ = do { takeMVar uniqSem+ ; n <- readIORef uniq+ ; writeIORef uniq $! (n + 1)+ ; putMVar uniqSem ()+ ; return n+ }++peepUniq :: IO Int+peepUniq = readIORef uniq++-- locals+{-# NOINLINE uniq #-}+uniq :: IORef Int+uniq = unsafePerformIO $ newIORef 1++{-# NOINLINE uniqSem #-}+uniqSem :: MVar ()+uniqSem = unsafePerformIO $ newMVar ()++{-+************************************************************************+* *+ Global, initualizers, etc+* *+************************************************************************+-}++openObserveGlobal :: IO ()+openObserveGlobal =+ do { initUniq+ ; startEventStream+ }++closeObserveGlobal :: IO [Event]+closeObserveGlobal =+ do { evs <- endEventStream+ ; putStrLn ""+ ; return evs+ }++{-+************************************************************************+* *+ The CDS and converting functions+* *+************************************************************************+-}++data CDS = CDSNamed String CDSSet+ | CDSCons Int String [CDSSet]+ | CDSFun Int CDSSet CDSSet+ | CDSEntered Int+ deriving (Show,Eq,Ord)++type CDSSet = [CDS]+++eventsToCDS :: [Event] -> CDSSet+eventsToCDS pairs = getChild 0 0+ where+ res i = (!) out_arr i++ bnds = (0, length pairs)++ mid_arr :: Array Int [(Int,CDS)]+ mid_arr = accumArray (flip (:)) [] bnds+ [ (pnode,(pport,res node))+ | (Event node (Parent pnode pport) _) <- pairs+ ]++ out_arr = array bnds -- never uses 0 index+ [ (node,getNode'' node change)+ | (Event node _ change) <- pairs+ ]++ getNode'' :: Int -> Change -> CDS+ getNode'' node change =+ case change of+ (Observe str) -> CDSNamed str (getChild node 0)+ (Enter) -> CDSEntered node+ (Fun) -> CDSFun node (getChild node 0) (getChild node 1)+ (Cons portc cons)+ -> CDSCons node cons+ [ getChild node n | n <- [0..(portc-1)]]++ getChild :: Int -> Int -> CDSSet+ getChild pnode pport =+ [ content+ | (pport',content) <- (!) mid_arr pnode+ , pport == pport'+ ]++render :: Int -> Bool -> CDS -> Doc+render prec par (CDSCons _ ":" [cds1,cds2]) =+ if (par && not needParen)+ then doc -- dont use paren (..) because we dont want a grp here!+ else paren needParen doc+ where+ doc = grp (softline <> renderSet' 5 False cds1 <> text " : ") <>+ renderSet' 4 True cds2+ needParen = prec > 4+render prec par (CDSCons _ "," cdss) | length cdss > 0 =+ nest 2 (text "(" <> foldl1 (\ a b -> a <> text ", " <> b)+ (map renderSet cdss) <>+ text ")")+render prec par (CDSCons _ name cdss) =+ paren (length cdss > 0 && prec /= 0)+ (nest 2+ (text name <> foldr (<>) nil+ [ softline <> renderSet' 10 False cds+ | cds <- cdss+ ]+ )+ )++{- renderSet handles the various styles of CDSSet.+ -}++renderSet :: CDSSet -> Doc+renderSet = renderSet' 0 False++renderSet' :: Int -> Bool -> CDSSet -> Doc+renderSet' _ _ [] = text "_"+renderSet' prec par [cons@(CDSCons {})] = render prec par cons+renderSet' prec par cdss =+ nest 0 (text "{ " <> foldl1 (\ a b -> a <> line <>+ text ", " <> b)+ (map renderFn pairs) <>+ line <> text "}")++ where+ pairs = nub (sort (findFn cdss))+ -- local nub for sorted lists+ nub [] = []+ nub (a:a':as) | a == a' = nub (a' : as)+ nub (a:as) = a : nub as++renderFn :: ([CDSSet],CDSSet) -> Doc+renderFn (args,res)+ = grp (nest 3+ (text "\\ " <>+ foldr (\ a b -> nest 0 (renderSet' 10 False a) <> sp <> b)+ nil+ args <> softline <>+ text "-> " <> renderSet' 0 False res+ )+ )++findFn :: CDSSet -> [([CDSSet],CDSSet)]+findFn = foldr findFn' []++findFn' (CDSFun _ arg res) rest =+ case findFn res of+ [(args',res')] -> (arg : args', res') : rest+ _ -> ([arg], res) : rest+findFn' other rest = ([],[other]) : rest++renderTops [] = nil+renderTops tops = line <> foldr (<>) nil (map renderTop tops)++renderTop :: Output -> Doc+renderTop (OutLabel str set extras) =+ nest 2 (text ("-- " ++ str) <> line <>+ renderSet set+ <> renderTops extras) <> line++rmEntry :: CDS -> CDS+rmEntry (CDSNamed str set) = CDSNamed str (rmEntrySet set)+rmEntry (CDSCons i str sets) = CDSCons i str (map rmEntrySet sets)+rmEntry (CDSFun i a b) = CDSFun i (rmEntrySet a) (rmEntrySet b)+rmEntry (CDSEntered i) = error "found bad CDSEntered"++rmEntrySet = map rmEntry . filter noEntered+ where+ noEntered (CDSEntered _) = False+ noEntered _ = True++simplifyCDS :: CDS -> CDS+simplifyCDS (CDSNamed str set) = CDSNamed str (simplifyCDSSet set)+simplifyCDS (CDSCons _ "throw"+ [[CDSCons _ "ErrorCall" set]]+ ) = simplifyCDS (CDSCons 0 "error" set)+simplifyCDS cons@(CDSCons i str sets) =+ case spotString [cons] of+ Just str | not (null str) -> CDSCons 0 (show str) []+ _ -> CDSCons 0 str (map simplifyCDSSet sets)++simplifyCDS (CDSFun i a b) = CDSFun 0 (simplifyCDSSet a) (simplifyCDSSet b)+ -- replace with+ -- CDSCons i "->" [simplifyCDSSet a,simplifyCDSSet b]+ -- for turning off the function stuff.++simplifyCDSSet = map simplifyCDS++spotString :: CDSSet -> Maybe String+spotString [CDSCons _ ":"+ [[CDSCons _ str []]+ ,rest+ ]+ ]+ = do { ch <- case reads str of+ [(ch,"")] -> return ch+ _ -> Nothing+ ; more <- spotString rest+ ; return (ch : more)+ }+spotString [CDSCons _ "[]" []] = return []+spotString other = Nothing++paren :: Bool -> Doc -> Doc+paren False doc = grp (nest 0 doc)+-- paren True doc = grp (nest 0 (text "(" <> nest 0 doc <> softline <> text ")"))+paren True doc = grp (text "(" <> doc <> softline <> text ")")++sp :: Doc+sp = text " "++data Output = OutLabel String CDSSet [Output]+ | OutData CDS+ deriving (Eq,Ord)+++commonOutput :: [Output] -> [Output]+commonOutput = sortBy byLabel+ where+ byLabel (OutLabel lab _ _) (OutLabel lab' _ _) = compare lab lab'++cdssToOutput :: CDSSet -> [Output]+cdssToOutput = map cdsToOutput++cdsToOutput (CDSNamed name cdsset)+ = OutLabel name res1 res2+ where+ res1 = [ cdss | (OutData cdss) <- res ]+ res2 = [ out | out@(OutLabel {}) <- res ]+ res = cdssToOutput cdsset+cdsToOutput cons@(CDSCons {}) = OutData cons+cdsToOutput fn@(CDSFun {}) = OutData fn++nil = Text.PrettyPrint.FPretty.empty+grp = Text.PrettyPrint.FPretty.group
− Debug/Hood/Observe.lhs
@@ -1,908 +0,0 @@-\begin{code}-{-# LANGUAGE Rank2Types #-}-\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--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.Hood.Observe- (- -- * The main Hood API--- observe -- (Observable a) => String -> a -> a- , Observer(..) -- contains a 'forall' typed observe (if supported).- , Observing -- a -> a- , Observable(..) -- Class- , runO -- IO a -> IO ()- , printO -- a -> IO ()- , putStrO -- String -> IO ()-- -- * For advanced users, that want to render their own datatypes.- , (<<) -- (Observable a) => ObserverM (a -> b) -> a -> ObserverM b- , thunk -- (Observable a) => a -> ObserverM a- , send- , observeBase- , observeOpaque-- -- * For users that want to write there own render drivers.-- , debugO -- IO a -> IO [CDS]- , CDS(..)- ) where-\end{code}---%************************************************************************-%* *-\subsection{Imports and infixing}-%* *-%************************************************************************--\begin{code}-import System.IO-import Data.Maybe-import Control.Applicative-import Control.Monad-import Data.Array as Array-import Data.List---import System---- The only non standard one we assume---import IOExts-import Data.IORef-import System.IO.Unsafe-\end{code}--\begin{code}-import Control.Concurrent-\end{code}--\begin{code}-import Control.Exception ( Exception, throw )-import qualified Control.Exception as Exception-{-- ( catch- , Exception(..)- , throw- ) as Exception--}-import Data.Dynamic ( Dynamic )-\end{code}--\begin{code}-import Prelude-\end{code}--\begin{code}-infixl 9 <<-\end{code}---%************************************************************************-%* *-\subsection{External start functions}-%* *-%************************************************************************--Run the observe ridden code.--\begin{code}--- | run some code and return the CDS structure (for when you want to write your own debugger).-debugO :: IO a -> IO [CDS]-debugO program =- do { initUniq- ; startEventStream- ; let errorMsg e = "[Escaping Exception in Code : " ++ show e ++ "]"- ; ourCatchAllIO (do { program ; return () })- (hPutStrLn stderr . errorMsg)- ; events <- endEventStream- ; return (eventsToCDS events)- }---- | print a value, with debugging-printO :: (Show a) => a -> IO ()-printO expr = runO (print expr)---- | print a string, with debugging-putStrO :: String -> IO ()-putStrO expr = runO (putStr expr)---- | The main entry point; run some IO code, and debug inside it.------ An example of using this debugger is------ @runO (print [ observe "+1" (+1) x | x <- observe "xs" [1..3]])@------ @[2,3,4]--- -- +1--- { \ 1 -> 2--- }--- -- +1--- { \ 2 -> 3--- }--- -- +1--- { \ 3 -> 4--- }--- -- xs--- 1 : 2 : 3 : []@------ Which says, the return is @[2,3,4]@, there were @3@ calls to +1--- (showing arguments and results), and @xs@, which was the list--- @1 : 2 : 3 : []@.-----runO :: IO a -> IO ()-runO program =- do { cdss <- debugO program- ; let cdss1 = rmEntrySet cdss- ; let cdss2 = simplifyCDSSet cdss1- ; let output1 = cdssToOutput cdss2- ; let output2 = commonOutput output1- ; let ptyout = pretty 80 (foldr (<>) nil (map renderTop output2))- ; hPutStrLn stderr ""- ; hPutStrLn stderr ptyout- }-\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 -> (Exception.SomeException -> IO a) -> IO a-ourCatchAllIO = Exception.catch--handleExc :: (Observable a) => Parent -> Exception.SomeException -> IO a-handleExc context exc = return (send "throw" (return throw << exc) context)-\end{code}---%************************************************************************-%* *-\subsection{Instances}-%* *-%************************************************************************-- The Haskell Base types--\begin{code}-instance Observable Int where { observer = observeBase }-instance Observable Bool where { observer = observeBase }-instance Observable Integer where { observer = observeBase }-instance Observable Float where { observer = observeBase }-instance Observable Double where { observer = observeBase }-instance Observable Char where { observer = observeBase }--instance Observable () where { observer = observeOpaque "()" }---- utilities for base types.--- The strictness (by using seq) is the same--- as the pattern matching done on other constructors.--- we evalute to WHNF, and not further.--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 (Right a) = send "Right" (return 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- )-\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-\end{code}---Functions.--\begin{code}-instance (Observable a,Observable b) => Observable (a -> b) where- observer fn cxt arg = sendObserveFnPacket (- do arg <- thunk arg- thunk (fn arg)) cxt-- observers = defaultFnObservers-\end{code}--The Exception *datatype* (not exceptions themselves!).-For now, we only display IOExceptions and calls to Error.--\begin{code}-instance Observable Exception.SomeException where--- observer (IOException a) = observeOpaque "IOException" (IOException a)--- observer (ErrorCall a) = send "ErrorCall" (return ErrorCall << a)- observer other = send "<Exception>" (return other)--instance Observable Dynamic where { observer = observeOpaque "<Dynamic>" }-\end{code}---%************************************************************************-%* *-\subsection{Classes and Data Definitions}-%* *-%************************************************************************--\begin{code}-class Observable a where- {-- - This reveals the name of a specific constructor.- - and gets ready to explain the sub-components.- -- - We put the context second so we can do eta-reduction- - with some of our definitions.- -}- observer :: a -> Parent -> a- {-- - This used used to group several observer instances together.- -}- observers :: String -> (Observer -> a) -> a- observers label arg = defaultObservers label arg--type Observing a = a -> a-\end{code}--\begin{code}-newtype Observer = O (forall a . (Observable a) => String -> a -> a)--defaultObservers :: (Observable a) => String -> (Observer -> a) -> a-defaultObservers label fn = unsafeWithUniq $ \ node ->- do { sendEvent node (Parent 0 0) (Observe label)- ; let observe' sublabel a- = unsafeWithUniq $ \ subnode ->- do { sendEvent subnode (Parent node 0)- (Observe sublabel)- ; return (observer_ a (Parent- { observeParent = subnode- , observePort = 0- }))- }- ; return (observer_ (fn (O observe'))- (Parent- { observeParent = node- , observePort = 0- }))- }-defaultFnObservers :: (Observable a, Observable b)- => String -> (Observer -> a -> b) -> a -> b-defaultFnObservers label fn arg = unsafeWithUniq $ \ node ->- do { sendEvent node (Parent 0 0) (Observe label)- ; let observe' sublabel a- = unsafeWithUniq $ \ subnode ->- do { sendEvent subnode (Parent node 0)- (Observe sublabel)- ; return (observer_ a (Parent- { observeParent = subnode- , observePort = 0- }))- }- ; return (observer_ (fn (O observe'))- (Parent- { observeParent = node- , observePort = 0- }) arg)- }-\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--instance Applicative ObserverM where- pure = return- (<*>) = ap--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 :: (Observable a) => a -> ObserverM a-thunk a = ObserverM $ \ parent port ->- ( observer_ a (Parent- { observeParent = parent- , observePort = port- })- , port+1 )--(<<) :: (Observable a) => ObserverM (a -> b) -> a -> ObserverM b-fn << a = do { fn' <- fn ; a' <- thunk a ; return (fn' a') }-\end{code}---%************************************************************************-%* *-\subsection{observe and friends}-%* *-%************************************************************************--Our principle function and class--\begin{code}--- | 'observe' observes data structures in flight.------ An example of use is--- @--- map (+1) . observe \"intermeduate\" . map (+2)--- @------ In this example, we observe the value that flows from the producer--- @map (+2)@ to the consumer @map (+1)@.------ 'observe' can also observe functions as well a structural values.----{-# NOINLINE observe #-}-observe :: (Observable a) => String -> a -> a-observe name a = generateContext name a--{- This gets called before observer, allowing us to mark- - we are entering a, before we do case analysis on- - our object.- -}--{-# NOINLINE observer_ #-}-observer_ :: (Observable a) => a -> Parent -> a-observer_ a context = sendEnterPacket a context-\end{code}--\begin{code}-data Parent = Parent- { observeParent :: !Int -- my parent- , observePort :: !Int -- my branch number- } deriving Show-root = Parent 0 0-\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 :: (Observable a) => String -> a -> a-generateContext label orig = unsafeWithUniq $ \ node ->- do { sendEvent node (Parent 0 0) (Observe label)- ; return (observer_ orig (Parent- { observeParent = node- , observePort = 0- })- )- }--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 :: (Observable a) => a -> Parent -> a-sendEnterPacket r context = unsafeWithUniq $ \ node ->- do { sendEvent node context Enter- ; ourCatchAllIO (evaluate (observer 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}-data Event = Event- { portId :: !Int- , parent :: !Parent- , change :: !Change- }- deriving Show--data Change- = Observe !String- | Cons !Int !String- | Enter- | Fun- deriving Show--startEventStream :: IO ()-startEventStream = writeIORef events []--endEventStream :: IO [Event]-endEventStream =- do { es <- readIORef events- ; writeIORef events badEvents- ; return es- }--sendEvent :: Int -> Parent -> Change -> IO ()-sendEvent nodeId parent change =- do { nodeId `seq` parent `seq` return ()- ; change `seq` return ()- ; takeMVar sendSem- ; es <- readIORef events- ; let event = Event nodeId parent change- ; writeIORef events (event `seq` (event : es))- ; putMVar sendSem ()- }---- local-events :: IORef [Event]-events = unsafePerformIO $ newIORef badEvents--badEvents :: [Event]-badEvents = error "Bad Event Stream"---- use as a trivial semiphore-{-# NOINLINE sendSem #-}-sendSem :: MVar ()-sendSem = unsafePerformIO $ newMVar ()--- end local-\end{code}---%************************************************************************-%* *-\subsection{unique name supply code}-%* *-%************************************************************************--Use the single threaded version--\begin{code}-initUniq :: IO ()-initUniq = writeIORef uniq 1--getUniq :: IO Int-getUniq- = do { takeMVar uniqSem- ; n <- readIORef uniq- ; writeIORef uniq $! (n + 1)- ; putMVar uniqSem ()- ; return n- }--peepUniq :: IO Int-peepUniq = readIORef uniq---- locals-{-# NOINLINE uniq #-}-uniq :: IORef Int-uniq = unsafePerformIO $ newIORef 1--{-# NOINLINE uniqSem #-}-uniqSem :: MVar ()-uniqSem = unsafePerformIO $ newMVar ()-\end{code}----%************************************************************************-%* *-\subsection{Global, initualizers, etc}-%* *-%************************************************************************--\begin{code}-openObserveGlobal :: IO ()-openObserveGlobal =- do { initUniq- ; startEventStream- }--closeObserveGlobal :: IO [Event]-closeObserveGlobal =- do { evs <- endEventStream- ; putStrLn ""- ; return evs- }-\end{code}---%************************************************************************-%* *-\subsection{The CDS and converting functions}-%* *-%************************************************************************--\begin{code}-data CDS = CDSNamed String CDSSet- | CDSCons Int String [CDSSet]- | CDSFun Int CDSSet CDSSet- | CDSEntered Int- deriving (Show,Eq,Ord)--type CDSSet = [CDS]---eventsToCDS :: [Event] -> CDSSet-eventsToCDS pairs = getChild 0 0- where- res i = (!) out_arr i-- bnds = (0, length pairs)-- mid_arr :: Array Int [(Int,CDS)]- mid_arr = accumArray (flip (:)) [] bnds- [ (pnode,(pport,res node))- | (Event node (Parent pnode pport) _) <- pairs- ]-- out_arr = array bnds -- never uses 0 index- [ (node,getNode'' node change)- | (Event node _ change) <- pairs- ]-- getNode'' :: Int -> Change -> CDS- getNode'' node change =- case change of- (Observe str) -> CDSNamed str (getChild node 0)- (Enter) -> CDSEntered node- (Fun) -> CDSFun node (getChild node 0) (getChild node 1)- (Cons portc cons)- -> CDSCons node cons- [ getChild node n | n <- [0..(portc-1)]]-- getChild :: Int -> Int -> CDSSet- getChild pnode pport =- [ content- | (pport',content) <- (!) mid_arr pnode- , pport == pport'- ]--render :: Int -> Bool -> CDS -> DOC-render prec par (CDSCons _ ":" [cds1,cds2]) =- if (par && not needParen)- then doc -- dont use paren (..) because we dont want a grp here!- else paren needParen doc- where- doc = grp (brk <> renderSet' 5 False cds1 <> text " : ") <>- renderSet' 4 True cds2- needParen = prec > 4-render prec par (CDSCons _ "," cdss) | length cdss > 0 =- nest 2 (text "(" <> foldl1 (\ a b -> a <> text ", " <> b)- (map renderSet cdss) <>- text ")")-render prec par (CDSCons _ name cdss) =- paren (length cdss > 0 && prec /= 0)- (nest 2- (text name <> foldr (<>) nil- [ sep <> renderSet' 10 False cds- | cds <- cdss- ]- )- )--{- renderSet handles the various styles of CDSSet.- -}--renderSet :: CDSSet -> DOC-renderSet = renderSet' 0 False--renderSet' :: Int -> Bool -> CDSSet -> DOC-renderSet' _ _ [] = text "_"-renderSet' prec par [cons@(CDSCons {})] = render prec par cons-renderSet' prec par cdss =- nest 0 (text "{ " <> foldl1 (\ a b -> a <> line <>- text ", " <> b)- (map renderFn pairs) <>- line <> text "}")-- where- pairs = nub (sort (findFn cdss))- -- local nub for sorted lists- nub [] = []- nub (a:a':as) | a == a' = nub (a' : as)- nub (a:as) = a : nub as--renderFn :: ([CDSSet],CDSSet) -> DOC-renderFn (args,res)- = grp (nest 3- (text "\\ " <>- foldr (\ a b -> nest 0 (renderSet' 10 False a) <> sp <> b)- nil- args <> sep <>- text "-> " <> renderSet' 0 False res- )- )--findFn :: CDSSet -> [([CDSSet],CDSSet)]-findFn = foldr findFn' []--findFn' (CDSFun _ arg res) rest =- case findFn res of- [(args',res')] -> (arg : args', res') : rest- _ -> ([arg], res) : rest-findFn' other rest = ([],[other]) : rest--renderTops [] = nil-renderTops tops = line <> foldr (<>) nil (map renderTop tops)--renderTop :: Output -> DOC-renderTop (OutLabel str set extras) =- nest 2 (text ("-- " ++ str) <> line <>- renderSet set- <> renderTops extras) <> line--rmEntry :: CDS -> CDS-rmEntry (CDSNamed str set) = CDSNamed str (rmEntrySet set)-rmEntry (CDSCons i str sets) = CDSCons i str (map rmEntrySet sets)-rmEntry (CDSFun i a b) = CDSFun i (rmEntrySet a) (rmEntrySet b)-rmEntry (CDSEntered i) = error "found bad CDSEntered"--rmEntrySet = map rmEntry . filter noEntered- where- noEntered (CDSEntered _) = False- noEntered _ = True--simplifyCDS :: CDS -> CDS-simplifyCDS (CDSNamed str set) = CDSNamed str (simplifyCDSSet set)-simplifyCDS (CDSCons _ "throw"- [[CDSCons _ "ErrorCall" set]]- ) = simplifyCDS (CDSCons 0 "error" set)-simplifyCDS cons@(CDSCons i str sets) =- case spotString [cons] of- Just str | not (null str) -> CDSCons 0 (show str) []- _ -> CDSCons 0 str (map simplifyCDSSet sets)--simplifyCDS (CDSFun i a b) = CDSFun 0 (simplifyCDSSet a) (simplifyCDSSet b)- -- replace with- -- CDSCons i "->" [simplifyCDSSet a,simplifyCDSSet b]- -- for turning off the function stuff.--simplifyCDSSet = map simplifyCDS--spotString :: CDSSet -> Maybe String-spotString [CDSCons _ ":"- [[CDSCons _ str []]- ,rest- ]- ]- = do { ch <- case reads str of- [(ch,"")] -> return ch- _ -> Nothing- ; more <- spotString rest- ; return (ch : more)- }-spotString [CDSCons _ "[]" []] = return []-spotString other = Nothing--paren :: Bool -> DOC -> DOC-paren False doc = grp (nest 0 doc)-paren True doc = grp (nest 0 (text "(" <> nest 0 doc <> brk <> text ")"))--sp :: DOC-sp = text " "--data Output = OutLabel String CDSSet [Output]- | OutData CDS- deriving (Eq,Ord)---commonOutput :: [Output] -> [Output]-commonOutput = sortBy byLabel- where- byLabel (OutLabel lab _ _) (OutLabel lab' _ _) = compare lab lab'--cdssToOutput :: CDSSet -> [Output]-cdssToOutput = map cdsToOutput--cdsToOutput (CDSNamed name cdsset)- = OutLabel name res1 res2- where- res1 = [ cdss | (OutData cdss) <- res ]- res2 = [ out | out@(OutLabel {}) <- res ]- res = cdssToOutput cdsset-cdsToOutput cons@(CDSCons {}) = OutData cons-cdsToOutput fn@(CDSFun {}) = OutData fn-\end{code}----%************************************************************************-%* *-\subsection{A Pretty Printer}-%* *-%************************************************************************--This pretty printer is based on Wadler's pretty printer.--\begin{code}-data DOC = NIL -- nil- | DOC :<> DOC -- beside- | NEST Int DOC- | TEXT String- | LINE -- always "\n"- | SEP -- " " or "\n"- | BREAK -- "" or "\n"- | DOC :<|> DOC -- choose one- deriving (Eq,Show)-data Doc = Nil- | Text Int String Doc- | Line Int Int Doc- deriving (Show,Eq)---mkText :: String -> Doc -> Doc-mkText s d = Text (toplen d + length s) s d--mkLine :: Int -> Doc -> Doc-mkLine i d = Line (toplen d + i) i d--toplen :: Doc -> Int-toplen Nil = 0-toplen (Text w s x) = w-toplen (Line w s x) = 0--nil = NIL-x <> y = x :<> y-nest i x = NEST i x-text s = TEXT s-line = LINE-sep = SEP-brk = BREAK--fold x = grp (brk <> x)--grp :: DOC -> DOC-grp x =- case flatten x of- Just x' -> x' :<|> x- Nothing -> x--flatten :: DOC -> Maybe DOC-flatten NIL = return NIL-flatten (x :<> y) =- do x' <- flatten x- y' <- flatten y- return (x' :<> y')-flatten (NEST i x) =- do x' <- flatten x- return (NEST i x')-flatten (TEXT s) = return (TEXT s)-flatten LINE = Nothing -- abort-flatten SEP = return (TEXT " ") -- SEP is space-flatten BREAK = return NIL -- BREAK is nil-flatten (x :<|> y) = flatten x--layout :: Doc -> String-layout Nil = ""-layout (Text _ s x) = s ++ layout x-layout (Line _ i x) = '\n' : replicate i ' ' ++ layout x--best w k doc = be w k [(0,doc)]--be :: Int -> Int -> [(Int,DOC)] -> Doc-be w k [] = Nil-be w k ((i,NIL):z) = be w k z-be w k ((i,x :<> y):z) = be w k ((i,x):(i,y):z)-be w k ((i,NEST j x):z) = be w k ((k+j,x):z)-be w k ((i,TEXT s):z) = s `mkText` be w (k+length s) z-be w k ((i,LINE):z) = i `mkLine` be w i z-be w k ((i,SEP):z) = i `mkLine` be w i z-be w k ((i,BREAK):z) = i `mkLine` be w i z-be w k ((i,x :<|> y):z) = better w k- (be w k ((i,x):z))- (be w k ((i,y):z))--better :: Int -> Int -> Doc -> Doc -> Doc-better w k x y = if (w-k) >= toplen x then x else y--pretty :: Int -> DOC -> String-pretty w x = layout (best w 0 x)-\end{code}-
hood.cabal view
@@ -1,5 +1,5 @@ Name: hood-Version: 0.2.1+Version: 0.3 Synopsis: Debugging by observing in place Description: Hood debugger, based on the idea of observing functions and structures as they are evaluated. Category: Debug, Trace@@ -7,16 +7,20 @@ License-file: LICENSE Author: Andy Gill Maintainer: Andy Gill <andygill@ku.edu>-Copyright: (c) 2000 Andy Gill, (c) 2010-2015 University of Kansas+Copyright: (c) 2000 Andy Gill, (c) 2010-2015 University of Kansas, (c) 2013-2015 Maarten Faddegon Homepage: http://www.ittc.ku.edu/csdl/fpg/Hood bug-reports: https://github.com/ku-fpg/hood/issues Stability: alpha build-type: Simple-Cabal-Version: >= 1.6+Cabal-Version: >= 1.8+tested-with: GHC == 7.4.2+ , GHC == 7.6.3+ , GHC == 7.8.4+ , GHC == 7.10.3 extra-source-files: CHANGELOG.md, README.md Library- Build-Depends: base >= 4 && < 5, array+ Build-Depends: base >= 4.5 && < 5, array, FPretty, ghc-prim Exposed-modules: Debug.Hood.Observe