packages feed

haskell-debugger 0.10.1.0 → 0.11.0.0

raw patch · 37 files changed

+2156/−533 lines, 37 filesdep +attoparsecdep +ghc-heapdep +tasty-expected-failuredep ~dapdep ~haskell-debugger-viewdep ~unixPVP ok

version bump matches the API change (PVP)

Dependencies added: attoparsec, ghc-heap, tasty-expected-failure

Dependency ranges changed: dap, haskell-debugger-view, unix

API changes (from Hackage documentation)

- GHC.Debugger.Evaluation: LogEvalModule :: Module -> EvalLog
- GHC.Debugger.Evaluation: continueToCompletion :: Debugger ExecResult
- GHC.Debugger.Evaluation: data EvalLog
- GHC.Debugger.Evaluation: debugExecution :: Recorder (WithSeverity EvalLog) -> FilePath -> EntryPoint -> [String] -> Debugger EvalResult
- GHC.Debugger.Evaluation: doContinue :: Debugger EvalResult
- GHC.Debugger.Evaluation: doEval :: String -> Debugger EvalResult
- GHC.Debugger.Evaluation: doLocalStep :: Debugger EvalResult
- GHC.Debugger.Evaluation: doSingleStep :: Debugger EvalResult
- GHC.Debugger.Evaluation: doStepOut :: Debugger EvalResult
- GHC.Debugger.Evaluation: handleExecResult :: ExecResult -> Debugger EvalResult
- GHC.Debugger.Evaluation: inspectName :: Name -> Debugger (Maybe VarInfo)
- GHC.Debugger.Evaluation: instance Prettyprinter.Internal.Pretty GHC.Debugger.Evaluation.EvalLog
- GHC.Debugger.Interface.Messages: StackFrame :: String -> SourceSpan -> StackFrame
- GHC.Debugger.Interface.Messages: data StackFrame
- GHC.Debugger.Interface.Messages: instance GHC.Internal.Show.Show GHC.Debugger.Interface.Messages.StackFrame
- GHC.Debugger.Monad: [termCache] :: DebuggerState -> IORef TermCache
- GHC.Debugger.Runtime.Instances: listTermToTermsList :: Term -> Debugger [Term]
- GHC.Debugger.Runtime.Term.Cache: insertTermCache :: TermKey -> Term -> TermCache -> TermCache
- GHC.Debugger.Runtime.Term.Cache: lookupTermCache :: TermKey -> TermCache -> Maybe Term
- GHC.Debugger.Runtime.Term.Cache: type TermCache = TermKeyMap Term
+ GHC.Debugger.Interface.Messages: DbgStackFrame :: String -> SourceSpan -> DbgStackFrame
+ GHC.Debugger.Interface.Messages: DebuggeeThread :: RemoteThreadId -> Maybe String -> DebuggeeThread
+ GHC.Debugger.Interface.Messages: GetThreads :: Command
+ GHC.Debugger.Interface.Messages: GotThreads :: [DebuggeeThread] -> Response
+ GHC.Debugger.Interface.Messages: RemoteThreadId :: Int -> RemoteThreadId
+ GHC.Debugger.Interface.Messages: [breakThread] :: EvalResult -> RemoteThreadId
+ GHC.Debugger.Interface.Messages: [remoteThreadIntRef] :: RemoteThreadId -> Int
+ GHC.Debugger.Interface.Messages: [tId] :: DebuggeeThread -> RemoteThreadId
+ GHC.Debugger.Interface.Messages: [tName] :: DebuggeeThread -> Maybe String
+ GHC.Debugger.Interface.Messages: data DbgStackFrame
+ GHC.Debugger.Interface.Messages: data DebuggeeThread
+ GHC.Debugger.Interface.Messages: instance GHC.Internal.Classes.Eq GHC.Debugger.Interface.Messages.RemoteThreadId
+ GHC.Debugger.Interface.Messages: instance GHC.Internal.Classes.Ord GHC.Debugger.Interface.Messages.RemoteThreadId
+ GHC.Debugger.Interface.Messages: instance GHC.Internal.Show.Show GHC.Debugger.Interface.Messages.DbgStackFrame
+ GHC.Debugger.Interface.Messages: instance GHC.Internal.Show.Show GHC.Debugger.Interface.Messages.DebuggeeThread
+ GHC.Debugger.Interface.Messages: instance GHC.Internal.Show.Show GHC.Debugger.Interface.Messages.RemoteThreadId
+ GHC.Debugger.Interface.Messages: newtype RemoteThreadId
+ GHC.Debugger.Monad: UnsupportedHsDbgViewVersion :: [Version] -> Version -> UnsupportedHsDbgViewVersion
+ GHC.Debugger.Monad: [actualVersion] :: UnsupportedHsDbgViewVersion -> Version
+ GHC.Debugger.Monad: [compCache] :: DebuggerState -> IORef CompCache
+ GHC.Debugger.Monad: [supportedVersions] :: UnsupportedHsDbgViewVersion -> [Version]
+ GHC.Debugger.Monad: [threadMap] :: DebuggerState -> IORef ThreadMap
+ GHC.Debugger.Monad: data UnsupportedHsDbgViewVersion
+ GHC.Debugger.Monad: instance GHC.Internal.Exception.Type.Exception GHC.Debugger.Monad.UnsupportedHsDbgViewVersion
+ GHC.Debugger.Monad: instance GHC.Internal.Show.Show GHC.Debugger.Monad.UnsupportedHsDbgViewVersion
+ GHC.Debugger.Run: LogEvalModule :: Module -> EvalLog
+ GHC.Debugger.Run: continueToCompletion :: Debugger ExecResult
+ GHC.Debugger.Run: data EvalLog
+ GHC.Debugger.Run: debugExecution :: Recorder (WithSeverity EvalLog) -> FilePath -> EntryPoint -> [String] -> Debugger EvalResult
+ GHC.Debugger.Run: doContinue :: Debugger EvalResult
+ GHC.Debugger.Run: doEval :: String -> Debugger EvalResult
+ GHC.Debugger.Run: doLocalStep :: Debugger EvalResult
+ GHC.Debugger.Run: doSingleStep :: Debugger EvalResult
+ GHC.Debugger.Run: doStepOut :: Debugger EvalResult
+ GHC.Debugger.Run: getRemoteThreadIdFromContext :: Debugger RemoteThreadId
+ GHC.Debugger.Run: handleExecResult :: ExecResult -> Debugger EvalResult
+ GHC.Debugger.Run: inspectName :: Name -> Debugger (Maybe VarInfo)
+ GHC.Debugger.Run: instance Prettyprinter.Internal.Pretty GHC.Debugger.Run.EvalLog
+ GHC.Debugger.Runtime.Compile: compileRaw :: String -> Debugger ForeignHValue
+ GHC.Debugger.Runtime.Compile: compileVar :: ModuleName -> String -> [String] -> Debugger ForeignHValue
+ GHC.Debugger.Runtime.Compile.Cache: data CompCache
+ GHC.Debugger.Runtime.Compile.Cache: emptyCompCache :: CompCache
+ GHC.Debugger.Runtime.Compile.Cache: insertCompRaw :: String -> ForeignHValue -> CompCache -> CompCache
+ GHC.Debugger.Runtime.Compile.Cache: lookupCompRaw :: String -> CompCache -> Maybe ForeignHValue
+ GHC.Debugger.Runtime.Eval: EvalHitUnexpectedBreakpoint :: BadEvalStatus
+ GHC.Debugger.Runtime.Eval: EvalRaisedException :: SomeException -> BadEvalStatus
+ GHC.Debugger.Runtime.Eval: EvalReturnedNoResults :: BadEvalStatus
+ GHC.Debugger.Runtime.Eval: EvalReturnedTooManyResults :: BadEvalStatus
+ GHC.Debugger.Runtime.Eval: data BadEvalStatus
+ GHC.Debugger.Runtime.Eval: evalExpr :: EvalExpr ForeignHValue -> Debugger (Either BadEvalStatus [ForeignHValue])
+ GHC.Debugger.Runtime.Eval: handleSingStatus :: Either BadEvalStatus [ForeignHValue] -> Either BadEvalStatus ForeignHValue
+ GHC.Debugger.Runtime.Eval: instance GHC.Internal.Exception.Type.Exception GHC.Debugger.Runtime.Eval.BadEvalStatus
+ GHC.Debugger.Runtime.Eval: instance GHC.Internal.Show.Show GHC.Debugger.Runtime.Eval.BadEvalStatus
+ GHC.Debugger.Runtime.Eval.RemoteExpr: (<$>) :: (RemoteExpr a -> RemoteExpr b) -> RemoteExpr (IO a) -> RemoteExpr (IO b)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: (>>) :: RemoteExpr (IO a) -> RemoteExpr (IO b) -> RemoteExpr (IO b)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: (>>=) :: RemoteExpr (IO a) -> (RemoteExpr a -> RemoteExpr (IO b)) -> RemoteExpr (IO b)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: app :: forall {l :: Levity} (a :: TYPE ('BoxedRep l)) b. RemoteExpr (a -> b) -> RemoteExpr a -> RemoteExpr b
+ GHC.Debugger.Runtime.Eval.RemoteExpr: appRef :: RemoteExpr (a -> b) -> ForeignRef a -> RemoteExpr b
+ GHC.Debugger.Runtime.Eval.RemoteExpr: data RemoteExpr (t :: TYPE 'BoxedRep l)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: eval :: RemoteExpr a -> Debugger (Either BadEvalStatus (ForeignRef a))
+ GHC.Debugger.Runtime.Eval.RemoteExpr: evalIO :: RemoteExpr (IO a) -> Debugger (Either BadEvalStatus (ForeignRef a))
+ GHC.Debugger.Runtime.Eval.RemoteExpr: evalIOList :: RemoteExpr (IO [a]) -> Debugger (Either BadEvalStatus [ForeignRef a])
+ GHC.Debugger.Runtime.Eval.RemoteExpr: evalIOString :: RemoteExpr (IO String) -> Debugger (Either BadEvalStatus String)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: evalString :: RemoteExpr String -> Debugger (Either BadEvalStatus String)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: fmap :: (RemoteExpr a -> RemoteExpr b) -> RemoteExpr (IO a) -> RemoteExpr (IO b)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: instance GHC.Internal.Show.Show (GHC.Debugger.Runtime.Eval.RemoteExpr.RemoteExpr a)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: lit :: Int -> RemoteExpr Int
+ GHC.Debugger.Runtime.Eval.RemoteExpr: pure :: RemoteExpr a -> RemoteExpr (IO a)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: raw :: String -> RemoteExpr a
+ GHC.Debugger.Runtime.Eval.RemoteExpr: ref :: ForeignRef a -> RemoteExpr a
+ GHC.Debugger.Runtime.Eval.RemoteExpr: return :: RemoteExpr a -> RemoteExpr (IO a)
+ GHC.Debugger.Runtime.Eval.RemoteExpr: untypedRef :: forall {l :: Levity} (a :: TYPE ('BoxedRep l)). ForeignHValue -> RemoteExpr a
+ GHC.Debugger.Runtime.Eval.RemoteExpr: var :: ModuleName -> String -> [String] -> RemoteExpr a
+ GHC.Debugger.Runtime.Eval.RemoteExpr: withUnboxed :: RemoteExpr Int -> RemoteExpr (Int# -> b) -> RemoteExpr b
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: cloneThreadStack :: RemoteExpr ThreadId -> RemoteExpr (IO StackSnapshot)
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: compose :: RemoteExpr (b -> c) -> RemoteExpr (a -> b) -> RemoteExpr (a -> c)
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: decode :: RemoteExpr StackSnapshot -> RemoteExpr (IO [StackEntry])
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: decodeStack :: RemoteExpr StackSnapshot -> RemoteExpr (IO StgStackClosure)
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: fromThreadId :: RemoteExpr ThreadId -> RemoteExpr Word64
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: getClosureData :: RemoteExpr StgStackClosure -> RemoteExpr (IO Closure)
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: indexAddrArray :: RemoteExpr ByteArray# -> RemoteExpr (Int# -> Ptr a)
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: listThreads :: RemoteExpr (IO [ThreadId])
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: maybeToList :: RemoteExpr (Maybe a) -> RemoteExpr [a]
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: peekCString :: RemoteExpr CString -> RemoteExpr (IO String)
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: ssc_stack :: RemoteExpr StgStackClosure -> RemoteExpr [StackFrame]
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: threadLabel :: RemoteExpr ThreadId -> RemoteExpr (IO (Maybe String))
+ GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin: threadStatus :: RemoteExpr ThreadId -> RemoteExpr (IO ThreadStatus)
+ GHC.Debugger.Runtime.Instances: VarValueResult :: String -> Bool -> VarValueResult
+ GHC.Debugger.Runtime.Instances: [varValueResultExpandable] :: VarValueResult -> Bool
+ GHC.Debugger.Runtime.Instances: [varValueResult] :: VarValueResult -> String
+ GHC.Debugger.Runtime.Instances: data VarValueResult
+ GHC.Debugger.Runtime.Instances.Discover: compileAndLoadMthd :: LHsExpr GhcPs -> Type -> TcM ForeignHValue
+ GHC.Debugger.Runtime.Term.Parser: TermParseError :: String -> TermParseError
+ GHC.Debugger.Runtime.Term.Parser: TermParser :: (Term -> Debugger (Either [TermParseError] a)) -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: [getTermErrorMessage] :: TermParseError -> String
+ GHC.Debugger.Runtime.Term.Parser: [runTermParser] :: TermParser a -> Term -> Debugger (Either [TermParseError] a)
+ GHC.Debugger.Runtime.Term.Parser: anyTerm :: TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: attachErrors :: [TermParseError] -> Debugger (Either [TermParseError] a) -> Debugger (Either [TermParseError] a)
+ GHC.Debugger.Runtime.Term.Parser: boolParser :: TermParser Bool
+ GHC.Debugger.Runtime.Term.Parser: checkType :: Type -> TermParser ()
+ GHC.Debugger.Runtime.Term.Parser: constructorName :: Either String DataCon -> String
+ GHC.Debugger.Runtime.Term.Parser: data TermParseError
+ GHC.Debugger.Runtime.Term.Parser: ensureTerm :: TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: focus :: TermParser Term -> TermParser a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: focusSeq :: HasCallStack => TermParser Term -> TermParser a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: foreignValueToTerm :: Type -> ForeignHValue -> TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Base.Alternative GHC.Debugger.Runtime.Term.Parser.TermParser
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Base.Applicative GHC.Debugger.Runtime.Term.Parser.TermParser
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Base.Functor GHC.Debugger.Runtime.Term.Parser.TermParser
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Base.Monad GHC.Debugger.Runtime.Term.Parser.TermParser
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Classes.Eq GHC.Debugger.Runtime.Term.Parser.TermParseError
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Control.Monad.Fail.MonadFail GHC.Debugger.Runtime.Term.Parser.TermParser
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Control.Monad.IO.Class.MonadIO GHC.Debugger.Runtime.Term.Parser.TermParser
+ GHC.Debugger.Runtime.Term.Parser: instance GHC.Internal.Show.Show GHC.Debugger.Runtime.Term.Parser.TermParseError
+ GHC.Debugger.Runtime.Term.Parser: intParser :: TermParser Int
+ GHC.Debugger.Runtime.Term.Parser: isSuspension :: TermParser Bool
+ GHC.Debugger.Runtime.Term.Parser: liftDebugger :: Debugger a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: logTermParserMsg :: String -> String -> Debugger ()
+ GHC.Debugger.Runtime.Term.Parser: matchConstructorTerm :: String -> TermParser ()
+ GHC.Debugger.Runtime.Term.Parser: matchDataConTerm :: DataCon -> a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: matchOccNameTerm :: String -> a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: maybeParser :: TermParser a -> TermParser (Maybe a)
+ GHC.Debugger.Runtime.Term.Parser: newtype TermParser a
+ GHC.Debugger.Runtime.Term.Parser: newtypeWrapParser :: TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: obtainParsedTerm :: String -> Int -> Bool -> Type -> ForeignHValue -> TermParser a -> Debugger (Either [TermParseError] a)
+ GHC.Debugger.Runtime.Term.Parser: parseError :: TermParseError -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: parseList :: TermParser a -> TermParser [a]
+ GHC.Debugger.Runtime.Term.Parser: primParser :: TermParser Word
+ GHC.Debugger.Runtime.Term.Parser: programTermParser :: TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: refreshTerm :: TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: reifyBool :: Bool -> Debugger ForeignHValue
+ GHC.Debugger.Runtime.Term.Parser: runTermParserLogged :: String -> TermParser a -> Term -> Debugger (Either [TermParseError] a)
+ GHC.Debugger.Runtime.Term.Parser: seqTermP :: HasCallStack => TermParser a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: stringParser :: TermParser String
+ GHC.Debugger.Runtime.Term.Parser: subtermTerm :: Int -> TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: subtermWith :: Int -> TermParser a -> TermParser a
+ GHC.Debugger.Runtime.Term.Parser: termTag :: Term -> String
+ GHC.Debugger.Runtime.Term.Parser: traceTerm :: TermParser ()
+ GHC.Debugger.Runtime.Term.Parser: tuple2Of :: TermParser a -> TermParser b -> TermParser (a, b)
+ GHC.Debugger.Runtime.Term.Parser: varFieldTupleParser :: TermParser (Term, Term)
+ GHC.Debugger.Runtime.Term.Parser: varFieldValueParser :: TermParser Term
+ GHC.Debugger.Runtime.Term.Parser: varFieldsParser :: TermParser [(String, Term)]
+ GHC.Debugger.Runtime.Term.Parser: varValueParser :: TermParser (Term, Bool)
+ GHC.Debugger.Runtime.Term.Parser: wordParser :: TermParser Word
+ GHC.Debugger.Runtime.Thread: getRemoteThreadId :: ForeignRef ThreadId -> Debugger RemoteThreadId
+ GHC.Debugger.Runtime.Thread: getRemoteThreadIdFromRemoteContext :: ForeignRef (ResumeContext [HValueRef]) -> Debugger RemoteThreadId
+ GHC.Debugger.Runtime.Thread: getRemoteThreadsLabels :: [ForeignRef ThreadId] -> Debugger [Maybe String]
+ GHC.Debugger.Runtime.Thread: listAllLiveRemoteThreads :: Debugger [(RemoteThreadId, ForeignRef ThreadId)]
+ GHC.Debugger.Runtime.Thread.Map: emptyThreadMap :: ThreadMap
+ GHC.Debugger.Runtime.Thread.Map: insertThreadMap :: Int -> ForeignRef ThreadId -> ThreadMap -> ThreadMap
+ GHC.Debugger.Runtime.Thread.Map: lookupThreadMap :: Int -> ThreadMap -> Maybe (ForeignRef ThreadId)
+ GHC.Debugger.Runtime.Thread.Map: threadMapToList :: ThreadMap -> [(Int, ForeignRef ThreadId)]
+ GHC.Debugger.Runtime.Thread.Map: type ThreadMap = IntMap ForeignRef ThreadId
+ GHC.Debugger.Runtime.Thread.Stack: getRemoteThreadIPEStack :: ForeignRef ThreadId -> Debugger [StackEntry]
+ GHC.Debugger.Runtime.Thread.Stack: getRemoteThreadStackCopy :: ForeignRef ThreadId -> Debugger [InternalBreakpointId]
+ GHC.Debugger.Runtime.Thread.Stack: instance GHC.Internal.Show.Show GHC.Debugger.Runtime.Thread.Stack.BCOBreakPointInfo
+ GHC.Debugger.Stopped: getThreads :: Debugger [DebuggeeThread]
+ GHC.Debugger.Utils: ($$) :: IsDoc doc => doc -> doc -> doc
+ GHC.Debugger.Utils: ($+$) :: SDoc -> SDoc -> SDoc
+ GHC.Debugger.Utils: (<+>) :: IsLine doc => doc -> doc -> doc
+ GHC.Debugger.Utils: (<>) :: IsLine doc => doc -> doc -> doc
+ GHC.Debugger.Utils: AllTheWay :: Depth
+ GHC.Debugger.Utils: CaseBind :: BindingSite
+ GHC.Debugger.Utils: CasePatBind :: BindingSite
+ GHC.Debugger.Utils: DefaultDepth :: Depth
+ GHC.Debugger.Utils: IsEmptyOrSingleton :: Bool -> IsEmptyOrSingleton
+ GHC.Debugger.Utils: JoinPoint :: Int -> JoinPointHood
+ GHC.Debugger.Utils: LambdaBind :: BindingSite
+ GHC.Debugger.Utils: LetBind :: BindingSite
+ GHC.Debugger.Utils: NameNotInScope1 :: QualifyName
+ GHC.Debugger.Utils: NameNotInScope2 :: QualifyName
+ GHC.Debugger.Utils: NameQual :: ModuleName -> QualifyName
+ GHC.Debugger.Utils: NameUnqual :: QualifyName
+ GHC.Debugger.Utils: NotJoinPoint :: JoinPointHood
+ GHC.Debugger.Utils: PDoc :: a -> PDoc a
+ GHC.Debugger.Utils: PartWay :: Int -> Depth
+ GHC.Debugger.Utils: PprCode :: PprStyle
+ GHC.Debugger.Utils: PprDump :: NamePprCtx -> PprStyle
+ GHC.Debugger.Utils: PprUser :: NamePprCtx -> Depth -> Coloured -> PprStyle
+ GHC.Debugger.Utils: PromTickCtx :: Bool -> Bool -> PromotionTickContext
+ GHC.Debugger.Utils: PromotedItemDataCon :: OccName -> PromotedItem
+ GHC.Debugger.Utils: PromotedItemListSyntax :: IsEmptyOrSingleton -> PromotedItem
+ GHC.Debugger.Utils: PromotedItemTupleSyntax :: PromotedItem
+ GHC.Debugger.Utils: QueryQualify :: QueryQualifyName -> QueryQualifyModule -> QueryQualifyPackage -> QueryPromotionTick -> NamePprCtx
+ GHC.Debugger.Utils: SDC :: PprStyle -> Scheme -> PprColour -> Bool -> Int -> Int -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> (FastString -> SDoc) -> SDocContext
+ GHC.Debugger.Utils: [ptcListTuplePuns] :: PromotionTickContext -> Bool
+ GHC.Debugger.Utils: [ptcPrintRedundantPromTicks] :: PromotionTickContext -> Bool
+ GHC.Debugger.Utils: [queryPromotionTick] :: NamePprCtx -> QueryPromotionTick
+ GHC.Debugger.Utils: [queryQualifyModule] :: NamePprCtx -> QueryQualifyModule
+ GHC.Debugger.Utils: [queryQualifyName] :: NamePprCtx -> QueryQualifyName
+ GHC.Debugger.Utils: [queryQualifyPackage] :: NamePprCtx -> QueryQualifyPackage
+ GHC.Debugger.Utils: [sdocCanUseUnicode] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocColScheme] :: SDocContext -> Scheme
+ GHC.Debugger.Utils: [sdocDefaultDepth] :: SDocContext -> Int
+ GHC.Debugger.Utils: [sdocErrorSpans] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocHexWordLiterals] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocLastColour] :: SDocContext -> PprColour
+ GHC.Debugger.Utils: [sdocLineLength] :: SDocContext -> Int
+ GHC.Debugger.Utils: [sdocLinearTypes] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocListTuplePuns] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPprDebug] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintAxiomIncomps] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintCaseAsLet] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintEqualityRelations] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintErrIndexLinks] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintExplicitCoercions] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintExplicitForalls] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintExplicitKinds] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintExplicitRuntimeReps] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintPotentialInstances] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintTypeAbbreviations] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintTypecheckerElaboration] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocPrintUnicodeSyntax] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocShouldUseColor] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocStarIsType] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocStyle] :: SDocContext -> PprStyle
+ GHC.Debugger.Utils: [sdocSuppressCoercionTypes] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressCoercions] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressIdInfo] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressModulePrefixes] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressStgExts] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressStgReps] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressTicks] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressTypeApplications] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressTypeSignatures] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressUnfoldings] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressUniques] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocSuppressVarKinds] :: SDocContext -> Bool
+ GHC.Debugger.Utils: [sdocUnitIdForUser] :: SDocContext -> FastString -> SDoc
+ GHC.Debugger.Utils: alwaysPrintPromTick :: QueryPromotionTick
+ GHC.Debugger.Utils: alwaysQualify :: NamePprCtx
+ GHC.Debugger.Utils: alwaysQualifyModules :: QueryQualifyModule
+ GHC.Debugger.Utils: alwaysQualifyNames :: QueryQualifyName
+ GHC.Debugger.Utils: alwaysQualifyPackages :: QueryQualifyPackage
+ GHC.Debugger.Utils: angleBrackets :: IsLine doc => doc -> doc
+ GHC.Debugger.Utils: arrow :: SDoc
+ GHC.Debugger.Utils: arrowt :: SDoc
+ GHC.Debugger.Utils: arrowtt :: SDoc
+ GHC.Debugger.Utils: bPutHDoc :: BufHandle -> SDocContext -> HDoc -> IO ()
+ GHC.Debugger.Utils: blankLine :: SDoc
+ GHC.Debugger.Utils: bndrIsJoin_maybe :: OutputableBndr a => a -> JoinPointHood
+ GHC.Debugger.Utils: braces :: IsLine doc => doc -> doc
+ GHC.Debugger.Utils: brackets :: IsLine doc => doc -> doc
+ GHC.Debugger.Utils: bufLeftRenderSDoc :: SDocContext -> BufHandle -> SDoc -> IO ()
+ GHC.Debugger.Utils: bullet :: SDoc
+ GHC.Debugger.Utils: cat :: [SDoc] -> SDoc
+ GHC.Debugger.Utils: class (IsOutput doc, IsLine Line doc) => IsDoc doc where {
+ GHC.Debugger.Utils: class IsOutput doc => IsLine doc
+ GHC.Debugger.Utils: class IsOutput doc
+ GHC.Debugger.Utils: class Outputable a
+ GHC.Debugger.Utils: class Outputable a => OutputableBndr a
+ GHC.Debugger.Utils: class OutputableP env a
+ GHC.Debugger.Utils: cmdlineParserStyle :: PprStyle
+ GHC.Debugger.Utils: codeStyle :: PprStyle -> Bool
+ GHC.Debugger.Utils: colon :: IsLine doc => doc
+ GHC.Debugger.Utils: coloured :: PprColour -> SDoc -> SDoc
+ GHC.Debugger.Utils: comma :: IsLine doc => doc
+ GHC.Debugger.Utils: cparen :: Bool -> SDoc -> SDoc
+ GHC.Debugger.Utils: darrow :: SDoc
+ GHC.Debugger.Utils: data BindingSite
+ GHC.Debugger.Utils: data Depth
+ GHC.Debugger.Utils: data HDoc
+ GHC.Debugger.Utils: data HLine
+ GHC.Debugger.Utils: data JoinPointHood
+ GHC.Debugger.Utils: data NamePprCtx
+ GHC.Debugger.Utils: data PprStyle
+ GHC.Debugger.Utils: data PromotedItem
+ GHC.Debugger.Utils: data PromotionTickContext
+ GHC.Debugger.Utils: data QualifyName
+ GHC.Debugger.Utils: data SDoc
+ GHC.Debugger.Utils: data SDocContext
+ GHC.Debugger.Utils: dcolon :: SDoc
+ GHC.Debugger.Utils: defaultDumpStyle :: PprStyle
+ GHC.Debugger.Utils: defaultErrStyle :: PprStyle
+ GHC.Debugger.Utils: defaultSDocContext :: SDocContext
+ GHC.Debugger.Utils: defaultUserStyle :: PprStyle
+ GHC.Debugger.Utils: doOrDoes :: [a] -> SDoc
+ GHC.Debugger.Utils: docToSDoc :: Doc -> SDoc
+ GHC.Debugger.Utils: docWithContext :: IsOutput doc => (SDocContext -> doc) -> doc
+ GHC.Debugger.Utils: docWithStyle :: IsOutput doc => doc -> (PprStyle -> SDoc) -> doc
+ GHC.Debugger.Utils: dot :: IsLine doc => doc
+ GHC.Debugger.Utils: double :: IsLine doc => Double -> doc
+ GHC.Debugger.Utils: doublePrec :: Int -> Double -> SDoc
+ GHC.Debugger.Utils: doubleQuotes :: IsLine doc => doc -> doc
+ GHC.Debugger.Utils: dualDoc :: IsDoc doc => SDoc -> HDoc -> doc
+ GHC.Debugger.Utils: dualLine :: IsLine doc => SDoc -> HLine -> doc
+ GHC.Debugger.Utils: dumpStyle :: PprStyle -> Bool
+ GHC.Debugger.Utils: empty :: IsOutput doc => doc
+ GHC.Debugger.Utils: equals :: IsLine doc => doc
+ GHC.Debugger.Utils: expectRight :: Exception e => Either e a -> Debugger a
+ GHC.Debugger.Utils: fcat :: [SDoc] -> SDoc
+ GHC.Debugger.Utils: float :: IsLine doc => Float -> doc
+ GHC.Debugger.Utils: forAllLit :: SDoc
+ GHC.Debugger.Utils: fsep :: IsLine doc => [doc] -> doc
+ GHC.Debugger.Utils: ftext :: IsLine doc => FastString -> doc
+ GHC.Debugger.Utils: getPprDebug :: IsOutput doc => (Bool -> doc) -> doc
+ GHC.Debugger.Utils: getPprStyle :: (PprStyle -> SDoc) -> SDoc
+ GHC.Debugger.Utils: hang :: SDoc -> Int -> SDoc -> SDoc
+ GHC.Debugger.Utils: hangNotEmpty :: SDoc -> Int -> SDoc -> SDoc
+ GHC.Debugger.Utils: hasOrHave :: [a] -> SDoc
+ GHC.Debugger.Utils: hcat :: IsLine doc => [doc] -> doc
+ GHC.Debugger.Utils: hsep :: IsLine doc => [doc] -> doc
+ GHC.Debugger.Utils: ifPprDebug :: IsOutput doc => doc -> doc -> doc
+ GHC.Debugger.Utils: int :: IsLine doc => Int -> doc
+ GHC.Debugger.Utils: intWithCommas :: Integral a => a -> SDoc
+ GHC.Debugger.Utils: integer :: IsLine doc => Integer -> doc
+ GHC.Debugger.Utils: interpp'SP :: Outputable a => [a] -> SDoc
+ GHC.Debugger.Utils: interpp'SP' :: (a -> SDoc) -> [a] -> SDoc
+ GHC.Debugger.Utils: interppSP :: Outputable a => [a] -> SDoc
+ GHC.Debugger.Utils: isEmpty :: SDocContext -> SDoc -> Bool
+ GHC.Debugger.Utils: isJoinPoint :: JoinPointHood -> Bool
+ GHC.Debugger.Utils: isListEmptyOrSingleton :: [a] -> IsEmptyOrSingleton
+ GHC.Debugger.Utils: isOrAre :: [a] -> SDoc
+ GHC.Debugger.Utils: itOrThey :: [a] -> SDoc
+ GHC.Debugger.Utils: itsOrTheir :: [a] -> SDoc
+ GHC.Debugger.Utils: keyword :: SDoc -> SDoc
+ GHC.Debugger.Utils: lambda :: SDoc
+ GHC.Debugger.Utils: larrow :: SDoc
+ GHC.Debugger.Utils: larrowt :: SDoc
+ GHC.Debugger.Utils: larrowtt :: SDoc
+ GHC.Debugger.Utils: lbrace :: IsLine doc => doc
+ GHC.Debugger.Utils: lbrack :: IsLine doc => doc
+ GHC.Debugger.Utils: line :: IsDoc doc => Line doc -> doc
+ GHC.Debugger.Utils: lines_ :: IsDoc doc => [Line doc] -> doc
+ GHC.Debugger.Utils: lollipop :: SDoc
+ GHC.Debugger.Utils: lparen :: IsLine doc => doc
+ GHC.Debugger.Utils: mkDumpStyle :: NamePprCtx -> PprStyle
+ GHC.Debugger.Utils: mkErrStyle :: NamePprCtx -> PprStyle
+ GHC.Debugger.Utils: mkUserStyle :: NamePprCtx -> Depth -> PprStyle
+ GHC.Debugger.Utils: nest :: Int -> SDoc -> SDoc
+ GHC.Debugger.Utils: neverQualify :: NamePprCtx
+ GHC.Debugger.Utils: neverQualifyModules :: QueryQualifyModule
+ GHC.Debugger.Utils: neverQualifyNames :: QueryQualifyName
+ GHC.Debugger.Utils: neverQualifyPackages :: QueryQualifyPackage
+ GHC.Debugger.Utils: newtype IsEmptyOrSingleton
+ GHC.Debugger.Utils: newtype PDoc a
+ GHC.Debugger.Utils: parens :: IsLine doc => doc -> doc
+ GHC.Debugger.Utils: pdoc :: OutputableP env a => env -> a -> SDoc
+ GHC.Debugger.Utils: plural :: [a] -> SDoc
+ GHC.Debugger.Utils: pluralSet :: Set a -> SDoc
+ GHC.Debugger.Utils: ppUnless :: IsOutput doc => Bool -> doc -> doc
+ GHC.Debugger.Utils: ppUnlessOption :: (SDocContext -> Bool) -> SDoc -> SDoc
+ GHC.Debugger.Utils: ppWhen :: IsOutput doc => Bool -> doc -> doc
+ GHC.Debugger.Utils: ppWhenOption :: (SDocContext -> Bool) -> SDoc -> SDoc
+ GHC.Debugger.Utils: ppr :: Outputable a => a -> SDoc
+ GHC.Debugger.Utils: pprBndr :: OutputableBndr a => BindingSite -> a -> SDoc
+ GHC.Debugger.Utils: pprCode :: SDoc -> SDoc
+ GHC.Debugger.Utils: pprDebugAndThen :: SDocContext -> (String -> a) -> SDoc -> SDoc -> a
+ GHC.Debugger.Utils: pprDeeper :: SDoc -> SDoc
+ GHC.Debugger.Utils: pprDeeperList :: ([SDoc] -> SDoc) -> [SDoc] -> SDoc
+ GHC.Debugger.Utils: pprFastFilePath :: FastString -> SDoc
+ GHC.Debugger.Utils: pprFilePathString :: IsLine doc => FilePath -> doc
+ GHC.Debugger.Utils: pprHsBytes :: ByteString -> SDoc
+ GHC.Debugger.Utils: pprHsChar :: Char -> SDoc
+ GHC.Debugger.Utils: pprHsString :: FastString -> SDoc
+ GHC.Debugger.Utils: pprInfixOcc :: OutputableBndr a => a -> SDoc
+ GHC.Debugger.Utils: pprInfixVar :: Bool -> SDoc -> SDoc
+ GHC.Debugger.Utils: pprModuleName :: IsLine doc => ModuleName -> doc
+ GHC.Debugger.Utils: pprPrefixOcc :: OutputableBndr a => a -> SDoc
+ GHC.Debugger.Utils: pprPrefixVar :: Bool -> SDoc -> SDoc
+ GHC.Debugger.Utils: pprPrimChar :: Char -> SDoc
+ GHC.Debugger.Utils: pprPrimInt :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimInt16 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimInt32 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimInt64 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimInt8 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimWord :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimWord16 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimWord32 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimWord64 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprPrimWord8 :: Integer -> SDoc
+ GHC.Debugger.Utils: pprQuotedList :: Outputable a => [a] -> SDoc
+ GHC.Debugger.Utils: pprSetDepth :: Depth -> SDoc -> SDoc
+ GHC.Debugger.Utils: pprUnquotedSet :: Outputable a => Set a -> SDoc
+ GHC.Debugger.Utils: pprWithBars :: (a -> SDoc) -> [a] -> SDoc
+ GHC.Debugger.Utils: pprWithCommas :: (a -> SDoc) -> [a] -> SDoc
+ GHC.Debugger.Utils: pprWithSemis :: (a -> SDoc) -> [a] -> SDoc
+ GHC.Debugger.Utils: primCharSuffix :: SDoc
+ GHC.Debugger.Utils: primDoubleSuffix :: SDoc
+ GHC.Debugger.Utils: primFloatSuffix :: SDoc
+ GHC.Debugger.Utils: primInt16Suffix :: SDoc
+ GHC.Debugger.Utils: primInt32Suffix :: SDoc
+ GHC.Debugger.Utils: primInt64Suffix :: SDoc
+ GHC.Debugger.Utils: primInt8Suffix :: SDoc
+ GHC.Debugger.Utils: primIntSuffix :: SDoc
+ GHC.Debugger.Utils: primWord16Suffix :: SDoc
+ GHC.Debugger.Utils: primWord32Suffix :: SDoc
+ GHC.Debugger.Utils: primWord64Suffix :: SDoc
+ GHC.Debugger.Utils: primWord8Suffix :: SDoc
+ GHC.Debugger.Utils: primWordSuffix :: SDoc
+ GHC.Debugger.Utils: printSDoc :: SDocContext -> Mode -> Handle -> SDoc -> IO ()
+ GHC.Debugger.Utils: printSDocLn :: SDocContext -> Mode -> Handle -> SDoc -> IO ()
+ GHC.Debugger.Utils: promTick :: PprStyle -> QueryPromotionTick
+ GHC.Debugger.Utils: ptext :: PtrString -> SDoc
+ GHC.Debugger.Utils: punctuate :: IsLine doc => doc -> [doc] -> [doc]
+ GHC.Debugger.Utils: punctuateFinal :: IsLine doc => doc -> doc -> [doc] -> [doc]
+ GHC.Debugger.Utils: qualModule :: PprStyle -> QueryQualifyModule
+ GHC.Debugger.Utils: qualName :: PprStyle -> QueryQualifyName
+ GHC.Debugger.Utils: qualPackage :: PprStyle -> QueryQualifyPackage
+ GHC.Debugger.Utils: queryQual :: PprStyle -> NamePprCtx
+ GHC.Debugger.Utils: quote :: SDoc -> SDoc
+ GHC.Debugger.Utils: quoteIfPunsEnabled :: SDoc -> SDoc
+ GHC.Debugger.Utils: quotedListWithAnd :: [SDoc] -> SDoc
+ GHC.Debugger.Utils: quotedListWithNor :: [SDoc] -> SDoc
+ GHC.Debugger.Utils: quotedListWithOr :: [SDoc] -> SDoc
+ GHC.Debugger.Utils: quotes :: SDoc -> SDoc
+ GHC.Debugger.Utils: rational :: Rational -> SDoc
+ GHC.Debugger.Utils: rbrace :: IsLine doc => doc
+ GHC.Debugger.Utils: rbrack :: IsLine doc => doc
+ GHC.Debugger.Utils: reallyAlwaysQualify :: NamePprCtx
+ GHC.Debugger.Utils: reallyAlwaysQualifyNames :: QueryQualifyName
+ GHC.Debugger.Utils: renderWithContext :: SDocContext -> SDoc -> String
+ GHC.Debugger.Utils: rparen :: IsLine doc => doc
+ GHC.Debugger.Utils: runSDoc :: SDoc -> SDocContext -> Doc
+ GHC.Debugger.Utils: sdocOption :: (SDocContext -> a) -> (a -> SDoc) -> SDoc
+ GHC.Debugger.Utils: sdocWithContext :: (SDocContext -> SDoc) -> SDoc
+ GHC.Debugger.Utils: semi :: IsLine doc => doc
+ GHC.Debugger.Utils: sep :: IsLine doc => [doc] -> doc
+ GHC.Debugger.Utils: setStyleColoured :: Bool -> PprStyle -> PprStyle
+ GHC.Debugger.Utils: showPprUnsafe :: Outputable a => a -> String
+ GHC.Debugger.Utils: showSDocOneLine :: SDocContext -> SDoc -> String
+ GHC.Debugger.Utils: showSDocUnsafe :: SDoc -> String
+ GHC.Debugger.Utils: singular :: [a] -> SDoc
+ GHC.Debugger.Utils: space :: IsLine doc => doc
+ GHC.Debugger.Utils: spaceIfSingleQuote :: SDoc -> SDoc
+ GHC.Debugger.Utils: speakN :: Int -> SDoc
+ GHC.Debugger.Utils: speakNOf :: Int -> SDoc -> SDoc
+ GHC.Debugger.Utils: speakNth :: Int -> SDoc
+ GHC.Debugger.Utils: srcSpanStringToSourceSpan :: String -> Either String SourceSpan
+ GHC.Debugger.Utils: text :: IsLine doc => String -> doc
+ GHC.Debugger.Utils: thisOrThese :: [a] -> SDoc
+ GHC.Debugger.Utils: traceSDocContext :: SDocContext
+ GHC.Debugger.Utils: type Line doc = (r :: Type) | r -> doc;
+ GHC.Debugger.Utils: type QueryPromotionTick = PromotedItem -> Bool
+ GHC.Debugger.Utils: type QueryQualifyModule = Module -> Bool
+ GHC.Debugger.Utils: type QueryQualifyName = Module -> Maybe ModuleName -> OccName -> QualifyName
+ GHC.Debugger.Utils: type QueryQualifyPackage = Unit -> Bool
+ GHC.Debugger.Utils: type family Line doc = (r :: Type) | r -> doc
+ GHC.Debugger.Utils: underscore :: IsLine doc => doc
+ GHC.Debugger.Utils: unicodeSyntax :: SDoc -> SDoc -> SDoc
+ GHC.Debugger.Utils: unquotedListWith :: SDoc -> [SDoc] -> SDoc
+ GHC.Debugger.Utils: updSDocContext :: (SDocContext -> SDocContext) -> SDoc -> SDoc
+ GHC.Debugger.Utils: userStyle :: PprStyle -> Bool
+ GHC.Debugger.Utils: vbar :: IsLine doc => doc
+ GHC.Debugger.Utils: vcat :: IsDoc doc => [doc] -> doc
+ GHC.Debugger.Utils: whenPprDebug :: IsOutput doc => doc -> doc
+ GHC.Debugger.Utils: withErrStyle :: NamePprCtx -> SDoc -> SDoc
+ GHC.Debugger.Utils: withPprStyle :: PprStyle -> SDoc -> SDoc
+ GHC.Debugger.Utils: withUserStyle :: NamePprCtx -> Depth -> SDoc -> SDoc
+ GHC.Debugger.Utils: word :: Integer -> SDoc
+ GHC.Debugger.Utils: word64 :: IsLine doc => Word64 -> doc
+ GHC.Debugger.Utils: ztext :: IsLine doc => FastZString -> doc
+ GHC.Debugger.Utils: }
- GHC.Debugger.Interface.Messages: EvalStopped :: Maybe InternalBreakpointId -> EvalResult
+ GHC.Debugger.Interface.Messages: EvalStopped :: Maybe InternalBreakpointId -> RemoteThreadId -> EvalResult
- GHC.Debugger.Interface.Messages: GetStacktrace :: Command
+ GHC.Debugger.Interface.Messages: GetStacktrace :: RemoteThreadId -> Command
- GHC.Debugger.Interface.Messages: GotStacktrace :: [StackFrame] -> Response
+ GHC.Debugger.Interface.Messages: GotStacktrace :: [DbgStackFrame] -> Response
- GHC.Debugger.Interface.Messages: [name] :: StackFrame -> String
+ GHC.Debugger.Interface.Messages: [name] :: DbgStackFrame -> String
- GHC.Debugger.Interface.Messages: [sourceSpan] :: StackFrame -> SourceSpan
+ GHC.Debugger.Interface.Messages: [sourceSpan] :: DbgStackFrame -> SourceSpan
- GHC.Debugger.Monad: DebuggerState :: IORef (BreakpointMap (BreakpointStatus, BreakpointKind)) -> IORef (IntMap TermKey, TermKeyMap Int) -> IORef TermCache -> IORef RuntimeInstancesCache -> IORef Int -> Maybe UnitId -> Recorder (WithSeverity DebuggerMonadLog) -> DebuggerState
+ GHC.Debugger.Monad: DebuggerState :: IORef (BreakpointMap (BreakpointStatus, BreakpointKind)) -> IORef (IntMap TermKey, TermKeyMap Int) -> IORef RuntimeInstancesCache -> IORef ThreadMap -> IORef CompCache -> IORef Int -> Maybe UnitId -> Recorder (WithSeverity DebuggerMonadLog) -> DebuggerState
- GHC.Debugger.Runtime.Instances: debugValueTerm :: Term -> Debugger (Maybe VarValue)
+ GHC.Debugger.Runtime.Instances: debugValueTerm :: Term -> Debugger (Maybe VarValueResult)
- GHC.Debugger.Stopped: getStacktrace :: Debugger [StackFrame]
+ GHC.Debugger.Stopped: getStacktrace :: RemoteThreadId -> Debugger [DbgStackFrame]
- GHC.Debugger.Stopped.Variables: forceTerm :: TermKey -> Term -> Debugger Term
+ GHC.Debugger.Stopped.Variables: forceTerm :: Term -> Debugger Term

Files

CHANGELOG.md view
@@ -1,5 +1,25 @@ # Revision history for haskell-debugger +## 0.11.0.0 -- 2026-01-05++* Introduce but don't yet expose infrastructure for debugging multi-threaded+  programs and constructing better callstacks.+* Introduce `RemoteExpr`: A DSL for evaluation on the remote process.+    * This allows us to cache remote expressions at a much greater granularity+      which improves performance and memory usage significantly in the long term.+* Improves logging to Debug Console+* Augment `haskell-debugger-version`, version `0.2.0.0` with+    * This release brings the `Program` DSL which allows the debuggee to be+      queried from a custom instance, making it possible to implement+      visualisations which rely on e.g. evaluatedness of a term.+        * See the `DebugView` instance for `[a]` for an example of a more+          complex instance.+    * `haskell-debugger-0.11.0.0` is only compatible with+      `haskell-debugger-view-0.2.0.0` and will abort if unsupported+      `haskell-debugger-view` versions are found in the dependencies closure+* Bump `dap` minimum version to `0.3.1`+* Various fixes and code improvements+ ## 0.10.1.0 -- 2025-11-21  * Fixes critical bug which prevented debugger from being used with non-boot
haskell-debugger-view/src/GHC/Debugger/View/ByteString.hs view
@@ -6,6 +6,6 @@ import qualified Data.ByteString    as BS  instance DebugView BS.ByteString where-  debugValue  t = VarValue (show t) False-  debugFields _ = VarFields []+  debugValue  t = simpleValue (show t) False+  debugFields _ = pure (VarFields []) 
haskell-debugger-view/src/GHC/Debugger/View/Class.hs view
@@ -20,7 +20,16 @@   , VarValue(..)   , VarFields(..)   , VarFieldValue(..)+  , simpleValue ++    -- * A 'Program' can describe a more complicated visualisation method which+    -- can query some information from the debugger.+  , Program(..)+  , isThunk+  , ifP++   -- * Utilities   --   -- | These can make it easier to write your own custom instances.@@ -63,17 +72,52 @@   --   -- This method should only be called to get the fields if the corresponding   -- @'VarValue'@ has @'varExpandable' = True@.-  debugFields :: a -> VarFields+  debugFields :: a -> Program VarFields +-- | The 'Program' abstraction allows more complicated 'DebugView' instances+-- to be constructed. The debugger will interpreter a 'Program' lazily when+-- determining how to display a variable.+--+-- At the moment the only interesting query when constructing a program is determining+-- if a value is already evaluated or not. This can be used to only display the evaluated+-- prefix of a list for example.+data Program a where+    -- | Lift a value to a program+    PureProgram :: a -> Program a+    -- | Program application+    ProgramAp :: Program (a -> b) -> Program a -> Program b+    -- | Evaluate the conditional, and branch on the result+    ProgramBranch :: Program Bool -> Program a -> Program a -> Program a+    -- | Is the value a thunk or evaluated?+    ProgramAskThunk :: a -> Program Bool++instance Functor Program where+   fmap f x = ProgramAp (PureProgram f) x++instance Applicative Program where+   pure = PureProgram+   fx <*> fy = ProgramAp fx fy++-- | Construct a 'VarValue' which doesn't require a 'Program'.+simpleValue :: String -> Bool -> VarValue+simpleValue s b = VarValue (pure s) b++-- | Construct a 'Program' which determines if 'a' is a thunk or not.+isThunk :: a -> Program Bool+isThunk = ProgramAskThunk++-- | Construct a program which branches+ifP :: Program Bool -> Program a -> Program a -> Program a+ifP = ProgramBranch+ -- | The representation of the value for some variable on the debugger data VarValue = VarValue   { -- | The value to display inline for this variable-    varValue      :: String+    varValue      :: Program String      -- | Can this variable further be expanded (s.t. @'debugFields'@ is not null?)   , varExpandable :: Bool   }-  deriving (Show, Read)  -- | The representation for fields of a value which is expandable in the debugger newtype VarFields = VarFields@@ -111,8 +155,8 @@ newtype BoringTy a = BoringTy a  instance Show a => DebugView (BoringTy a) where-  debugValue (BoringTy x) = VarValue (show x) False-  debugFields _           = VarFields []+  debugValue (BoringTy x) = simpleValue (show x) False+  debugFields _           = pure $ VarFields []  deriving via BoringTy Int     instance DebugView Int deriving via BoringTy Int8    instance DebugView Int8@@ -131,31 +175,48 @@ deriving via BoringTy String  instance DebugView String  instance DebugView (a, b) where-  debugValue _ = VarValue "( , )" True-  debugFields (x, y) = VarFields+  debugValue _ = simpleValue "( , )" True+  debugFields (x, y) = pure $ VarFields     [ ("fst", VarFieldValue x)     , ("snd", VarFieldValue y) ] +-- | This instance will display up to the first 50 forced elements of a list.+instance {-# OVERLAPPABLE #-} DebugView [a] where+  debugValue [] = simpleValue "[]" False+  debugValue (_:_) = simpleValue "[...]" True+  debugFields v = VarFields <$> go 0 v+    where+      go :: Int -> [a] -> Program [(String, VarFieldValue)]+      go 50 xs = pure [("tail", VarFieldValue xs)]+      go _ [] = pure []+      go n (x:xs) = ((show n, VarFieldValue x) :) <$>+                      (ifP (isThunk xs) (pure $ [("tail", VarFieldValue xs)])+                                        (go (n + 1) xs))+ -------------------------------------------------------------------------------- -- * (Internal) Wrappers required to call `evalStmt` on methods more easily --------------------------------------------------------------------------------  -- | Wrapper to make evaluating from debugger easier data VarValueIO = VarValueIO-  { varValueIO :: IO String+  { varValueIO :: Program (IO String)   , varExpandableIO :: Bool   }  debugValueIOWrapper :: DebugView a => a -> IO [VarValueIO] debugValueIOWrapper x = case debugValue x of   VarValue str b ->-    pure [VarValueIO (pure str) b]+    pure [VarValueIO (pure <$> str) b]  newtype VarFieldsIO = VarFieldsIO-  { varFieldsIO :: [(IO String, VarFieldValue)]+  { varFieldsIO :: Program [(IO String, VarFieldValue)]   }  debugFieldsIOWrapper :: DebugView a => a -> IO [VarFieldsIO]-debugFieldsIOWrapper x = case debugFields x of-  VarFields fls ->-    pure [VarFieldsIO [ (pure fl_s, b) | (fl_s, b) <- fls]]+debugFieldsIOWrapper x = pure [VarFieldsIO (toVarFieldsIO <$> (debugFields x))]++toVarFieldsIO :: VarFields -> [(IO String, VarFieldValue)]+toVarFieldsIO x =+  case x of+    VarFields fls -> [ (pure fl_s, b) | (fl_s, b) <- fls]+
haskell-debugger-view/src/GHC/Debugger/View/Containers.hs view
@@ -7,15 +7,15 @@ import qualified Data.Map           as M  instance DebugView (IM.IntMap a) where-  debugValue _ = VarValue "IntMap" True-  debugFields im = VarFields+  debugValue _ = simpleValue "IntMap" True+  debugFields im = pure $ VarFields     [ (show k, VarFieldValue v)     | (k, v) <- IM.toList im     ]  instance Show k => DebugView (M.Map k a) where-  debugValue _ = VarValue "Map" True-  debugFields m = VarFields+  debugValue _ = simpleValue "Map" True+  debugFields m = pure $ VarFields     [ (show k, VarFieldValue v)     | (k, v) <- M.toList m     ]
haskell-debugger-view/src/GHC/Debugger/View/Text.hs view
@@ -6,6 +6,6 @@ import qualified Data.Text          as T  instance DebugView T.Text where-  debugValue  t = VarValue (show (T.unpack t)) False-  debugFields _ = VarFields []+  debugValue  t = simpleValue (show (T.unpack t)) False+  debugFields _ = pure $ VarFields [] 
haskell-debugger.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.12 name:               haskell-debugger-version:            0.10.1.0+version:            0.11.0.0 synopsis:     A step-through debugger for GHC Haskell @@ -58,19 +58,33 @@     exposed-modules:  GHC.Debugger,                       GHC.Debugger.Breakpoint,                       GHC.Debugger.Breakpoint.Map,-                      GHC.Debugger.Evaluation,                       GHC.Debugger.Logger,+                      GHC.Debugger.Run,                       GHC.Debugger.Stopped,                       GHC.Debugger.Stopped.Variables,-                      GHC.Debugger.Utils,+                       GHC.Debugger.Runtime,++                      GHC.Debugger.Runtime.Eval,+                      GHC.Debugger.Runtime.Eval.RemoteExpr,+                      GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin,+                       GHC.Debugger.Runtime.Instances,                       GHC.Debugger.Runtime.Instances.Discover, +                      GHC.Debugger.Runtime.Compile,+                      GHC.Debugger.Runtime.Compile.Cache,++                      GHC.Debugger.Runtime.Term.Parser,                       GHC.Debugger.Runtime.Term.Key,                       GHC.Debugger.Runtime.Term.Cache, +                      GHC.Debugger.Runtime.Thread,+                      GHC.Debugger.Runtime.Thread.Stack,+                      GHC.Debugger.Runtime.Thread.Map,+                       GHC.Debugger.Monad,+                      GHC.Debugger.Utils,                        GHC.Debugger.Session,                       GHC.Debugger.Session.Builtin,@@ -81,12 +95,12 @@                       ghc >= 9.14 && < 9.16, ghci >= 9.14 && < 9.16,                       ghc-boot-th >= 9.14 && < 9.16,                       ghc-boot >= 9.14 && < 9.16,+                      ghc-heap >= 9.14 && < 9.16,                       array >= 0.5.8 && < 0.6,                       containers >= 0.7 && < 0.9,                       mtl >= 2.3 && < 3,                       binary >= 0.8.9 && < 0.11,                       process >= 1.6.25 && < 1.7,-                      unix >= 2.8.6 && < 2.9,                       filepath >= 1.5.4 && < 1.6,                       directory >= 1.3.9.0 && < 1.4,                       exceptions >= 0.10.9 && < 0.11,@@ -96,14 +110,18 @@                       aeson >= 2.2.3 && < 2.3,                       hie-bios >= 0.15 && < 0.18,                       file-embed >= 0.0.16 && < 0.1,+                      attoparsec >= 0.13 && < 0.15,                       -- Logger dependencies                       time >= 1.14 && < 2,                       prettyprinter >= 1.7.1 && < 2,                       text >= 2.1 && < 2.3,                       co-log-core >= 0.3.2.5 && < 0.4, -                      haskell-debugger-view >= 0.1 && < 1.0+                      haskell-debugger-view >= 0.2 && < 1.0 +    if !os(windows)+        build-depends: unix >= 2.8.6 && < 2.9,+     hs-source-dirs:   haskell-debugger     default-language: GHC2021 @@ -136,7 +154,7 @@         base, ghc,         exceptions, aeson, bytestring,         containers, filepath,-        process, mtl, unix,+        process, mtl,         unordered-containers >= 0.2.19 && < 0.3,          haskell-debugger,@@ -152,7 +170,7 @@         network-run >= 0.4.4,         async >= 2.2.5 && < 2.3,         text >= 2.1 && < 2.3,-        dap >= 0.3 && < 1,+        dap >= 0.3.1 && < 0.4,          haskeline >= 0.8 && < 1,         optparse-applicative >= 0.18 && < 0.20@@ -186,6 +204,7 @@         tasty >= 1.5.3,         tasty-golden >= 2.3.5,         tasty-hunit >= 0.10.2,+        tasty-expected-failure >= 0.12.3,         regex >= 1.1     build-tool-depends: haskell-debugger:hdb     ghc-options: -threaded
haskell-debugger/GHC/Debugger.hs view
@@ -7,7 +7,7 @@ import Control.Monad.IO.Class  import GHC.Debugger.Breakpoint-import GHC.Debugger.Evaluation+import GHC.Debugger.Run import GHC.Debugger.Stopped import GHC.Debugger.Monad import GHC.Debugger.Interface.Messages@@ -26,7 +26,8 @@     DidSetBreakpoint <$> setBreakpoint brk (condBreakEnableStatus hitCount condition)   DelBreakpoint bp -> DidRemoveBreakpoint <$> setBreakpoint bp BreakpointDisabled   GetBreakpointsAt bp -> DidGetBreakpoints <$> getBreakpointsAt bp-  GetStacktrace -> GotStacktrace <$> getStacktrace+  GetThreads -> GotThreads <$> getThreads+  GetStacktrace i -> GotStacktrace <$> getStacktrace i   GetScopes -> GotScopes <$> getScopes   GetVariables kind -> GotVariables <$> getVariables kind   DoEval exp_s -> DidEval <$> doEval exp_s
− haskell-debugger/GHC/Debugger/Evaluation.hs
@@ -1,250 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE ViewPatterns #-}-module GHC.Debugger.Evaluation where--import GHC.Utils.Outputable-import Control.Monad.IO.Class-import Control.Monad.Catch-import Control.Monad.Reader-import Data.IORef-import qualified Data.List as List-import Data.Maybe-import System.FilePath-import System.Directory-import qualified Prettyprinter as Pretty--import GHC-import GHC.Builtin.Names (gHC_INTERNAL_GHCI_HELPERS)-import GHC.Unit.Types-import GHC.Data.FastString-import GHC.Driver.DynFlags as GHC-import GHC.Driver.Monad as GHC-import GHC.Driver.Env as GHC-import GHC.Runtime.Debugger.Breakpoints as GHC-import qualified GHC.Unit.Module.ModSummary as GHC-import GHC.Types.Name.Occurrence (mkVarOccFS)-import GHC.Types.Name.Reader as RdrName (mkOrig)-import GHC.Utils.Outputable as GHC-import qualified GHCi.Message as GHCi-import qualified GHC.Data.Strict as Strict--import GHC.Debugger.Stopped.Variables-import GHC.Debugger.Monad-import GHC.Debugger.Utils-import GHC.Debugger.Interface.Messages-import GHC.Debugger.Logger as Logger-import qualified GHC.Debugger.Breakpoint.Map as BM--data EvalLog-  = LogEvalModule GHC.Module--instance Pretty EvalLog where-  pretty = \ case-    LogEvalModule modl -> "Eval Module Context:" Pretty.<+> pretty (GHC.showSDocUnsafe (ppr modl))------------------------------------------------------------------------------------- * Evaluation------------------------------------------------------------------------------------- | Run a program with debugging enabled-debugExecution :: Recorder (WithSeverity EvalLog) -> FilePath -> EntryPoint -> [String] {-^ Args -} -> Debugger EvalResult-debugExecution recorder entryFile entry args = do-  -- consider always using :trace like ghci-dap to always have a stacktrace?-  -- better solution could involve profiling stack traces or from IPE info?-  modSummaryOfEntryFile <- findUnitIdOfEntryFile entryFile-  let modOfEntryFile = GHC.ms_mod modSummaryOfEntryFile-      unitIdOfEntryFile = GHC.ms_unitid modSummaryOfEntryFile--  let-    evalModule = mkModule (RealUnit (Definite unitIdOfEntryFile))-                                         (moduleName modOfEntryFile)--  logWith recorder Info $ LogEvalModule evalModule-  old_context <- GHC.getContext-  GHC.setContext [GHC.IIModule evalModule]--  (entryExp, exOpts) <- case entry of-    MainEntry nm -> do-      let prog = fromMaybe "main" nm-      -- the wrapper is equivalent to GHCi's `:main arg1 arg2 arg3`-      wrapper <- mkEvalWrapper prog args -- bit weird that the prog name is the expression but fine-      let execWrap' fhv = GHCi.EvalApp (GHCi.EvalThis wrapper) (GHCi.EvalThis fhv)-          opts = GHC.execOptions {execWrap = execWrap'}-      return (prog, opts)--    FunctionEntry fn ->-      -- TODO: if "args" is unescaped (e.g. "some", "thing"), then "some" and-      -- "thing" will be interpreted as variables. To pass strings it needs to-      -- be "\"some\"" "\"things\"".-      return (fn ++ " " ++ unwords args, GHC.execOptions)--  exec_res <- GHC.execStmt entryExp exOpts-  GHC.setContext old_context -- restore context after running `main`-  handleExecResult exec_res-  where-    -- It's not ideal to duplicate these two functions from ghci, but its unclear where they would better live. Perhaps next to compileParsedExprRemote? The issue is run-    mkEvalWrapper :: GhcMonad m => String -> [String] -> m ForeignHValue-    mkEvalWrapper progname' args' =-      runInternal $ GHC.compileParsedExprRemote-      $ evalWrapper' `GHC.mkHsApp` nlHsString progname'-                     `GHC.mkHsApp` nlList (map nlHsString args')-      where-        nlHsString = nlHsLit . mkHsString-        evalWrapper' =-          GHC.nlHsVar $ RdrName.mkOrig gHC_INTERNAL_GHCI_HELPERS (mkVarOccFS (fsLit "evalWrapper"))--    -- run internal here serves to overwrite certain flags while executing the-    -- internal "evalWrapper" computation which is not relevant to the user.-    runInternal :: GhcMonad m => m a -> m a-    runInternal =-        withTempSession mkTempSession-      where-        mkTempSession = hscUpdateFlags (\dflags -> dflags-          { -- Disable dumping of any data during evaluation of GHCi's internal expressions. (#17500)-            dumpFlags = mempty-          }-              -- We depend on -fimplicit-import-qualified to compile expr-              -- with fully qualified names without imports (gHC_INTERNAL_GHCI_HELPERS above).-              `gopt_set` Opt_ImplicitImportQualified-          )--    findUnitIdOfEntryFile :: GhcMonad m => FilePath -> m GHC.ModSummary-    findUnitIdOfEntryFile fp = do-      afp <- normalise <$> liftIO (makeAbsolute fp)-      modSums <- getAllLoadedModules-      let normalisedModLoc = fmap normalise . GHC.ml_hs_file . GHC.ms_location-      case List.find ((Just afp ==) . normalisedModLoc) modSums of-        Nothing -> error $ "findUnitIdOfEntryFile: no unit id found for: " ++ fp ++ "\nCandidates were:\n" ++ unlines (map (show . normalisedModLoc) modSums)-        Just summary -> pure summary---- | Resume execution of the stopped debuggee program-doContinue :: Debugger EvalResult-doContinue = do-  leaveSuspendedState-  GHC.resumeExec RunToCompletion Nothing-    >>= handleExecResult---- | Resume execution but only take a single step.-doSingleStep :: Debugger EvalResult-doSingleStep = do-  leaveSuspendedState-  GHC.resumeExec SingleStep Nothing-    >>= handleExecResult--doStepOut :: Debugger EvalResult-doStepOut = do-  leaveSuspendedState-  mb_span <- getCurrentBreakSpan-  case mb_span of-    Nothing ->-      GHC.resumeExec (GHC.StepOut Nothing) Nothing-        >>= handleExecResult-    Just loc -> do-      md <- fromMaybe (error "doStepOut") <$> getCurrentBreakModule-      ticks <- fromMaybe (error "doLocalStep:getTicks") <$> makeModuleLineMap md-      let current_toplevel_decl = enclosingTickSpan ticks loc-      GHC.resumeExec (GHC.StepOut (Just (RealSrcSpan current_toplevel_decl Strict.Nothing))) Nothing-        >>= handleExecResult---- | Resume execution but stop at the next tick within the same function.------ To do a local step, we get the SrcSpan of the current suspension state and--- get its 'enclosingTickSpan' to use as a filter for breakpoints in the call--- to 'resumeExec'. Execution will only stop at breakpoints whose span matches--- this enclosing span.-doLocalStep :: Debugger EvalResult-doLocalStep = do-  leaveSuspendedState-  mb_span <- getCurrentBreakSpan-  case mb_span of-    Nothing -> error "not stopped at a breakpoint?!"-    Just (UnhelpfulSpan _) -> do-      liftIO $ putStrLn "Stopped at an exception. Forcing step into..."-      GHC.resumeExec SingleStep Nothing >>= handleExecResult-    Just loc -> do-      md <- fromMaybe (error "doLocalStep") <$> getCurrentBreakModule-      -- TODO: Cache moduleLineMap.-      ticks <- fromMaybe (error "doLocalStep:getTicks") <$> makeModuleLineMap md-      let current_toplevel_decl = enclosingTickSpan ticks loc-      GHC.resumeExec (LocalStep (RealSrcSpan current_toplevel_decl mempty)) Nothing >>= handleExecResult---- | Evaluate expression. Includes context of breakpoint if stopped at one (the current interactive context).-doEval :: String -> Debugger EvalResult-doEval expr = do-  excr <- (Right <$> GHC.execStmt expr GHC.execOptions) `catch` \(e::SomeException) -> pure (Left (displayException e))-  case excr of-    Left err -> pure $ EvalAbortedWith err-    Right ExecBreak{} -> continueToCompletion >>= handleExecResult-    Right r@ExecComplete{} -> handleExecResult r---- | Resume execution with single step mode 'RunToCompletion', skipping all breakpoints we hit, until we reach 'ExecComplete'.------ We use this in 'doEval' because we want to ignore breakpoints in expressions given at the prompt.-continueToCompletion :: Debugger GHC.ExecResult-continueToCompletion = do-  execr <- GHC.resumeExec GHC.RunToCompletion Nothing-  case execr of-    GHC.ExecBreak{} -> continueToCompletion-    GHC.ExecComplete{} -> return execr---- | Turn a GHC's 'ExecResult' into an 'EvalResult' response-handleExecResult :: GHC.ExecResult -> Debugger EvalResult-handleExecResult = \case-    ExecComplete {execResult} -> do-      case execResult of-        Left e -> return (EvalException (show e) "SomeException")-        Right [] -> return (EvalCompleted "" "" NoVariables) -- Evaluation completed without binding any result.-        Right (n:_ns) -> inspectName n >>= \case-          Just VarInfo{varValue, varType, varRef} -> do-            return (EvalCompleted varValue varType varRef)-          Nothing     -> liftIO $ fail "doEval failed"-    ExecBreak {breakNames = _, breakPointId = Nothing} ->-      -- Stopped at an exception-      -- TODO: force the exception to display string with Backtrace?-      return EvalStopped{breakId = Nothing}-    ExecBreak {breakNames = _, breakPointId = Just bid} -> do-      bm <- liftIO . readIORef =<< asks activeBreakpoints-      case BM.lookup bid bm of-        -- todo: BreakpointAfterCountCond is not handled yet.-        Just (BreakpointWhenCond cond, _) -> do-          let evalFailedMsg e = text $ "Evaluation of conditional breakpoint expression failed with " ++ e ++ "\nIgnoring..."-          let resume = GHC.resumeExec GHC.RunToCompletion Nothing >>= handleExecResult-          doEval cond >>= \case-            EvalStopped{} -> error "impossible for doEval"-            EvalCompleted { resultVal, resultType } ->-              if resultType == "Bool" then do-                if resultVal == "True" then-                  return EvalStopped{breakId = Just bid}-                else-                  resume-              else do-                logSDoc Logger.Warning (evalFailedMsg "\"expression resultType is != Bool\"")-                resume-            EvalException { resultVal } -> do-              logSDoc Logger.Warning (evalFailedMsg resultVal)-              resume-            EvalAbortedWith e -> do-              logSDoc Logger.Warning (evalFailedMsg e)-              resume--        -- Unconditionally 'EvalStopped' in all other cases-        _ -> return EvalStopped{breakId = Just bid}---- | Get the value and type of a given 'Name' as rendered strings in 'VarInfo'.-inspectName :: Name -> Debugger (Maybe VarInfo)-inspectName n = do-  GHC.lookupName n >>= \case-    Nothing -> do-      liftIO . putStrLn =<< display (text "Failed to lookup name: " <+> ppr n)-      pure Nothing-    Just tt -> Just <$> tyThingToVarInfo tt-
haskell-debugger/GHC/Debugger/Interface/Messages.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -Wno-orphans #-} -- TODO: drop this and Show GHC.InternalBreakpointId... {-# LANGUAGE LambdaCase,              StandaloneDeriving,              OverloadedStrings,@@ -39,8 +40,11 @@   -- | Clear all function breakpoints   | ClearFunctionBreakpoints +  -- | Get all threads+  | GetThreads+   -- | Get the evaluation stacktrace until the current breakpoint.-  | GetStacktrace+  | GetStacktrace RemoteThreadId    -- | Get the list of available scopes at the current breakpoint   | GetScopes@@ -208,7 +212,8 @@   | DidContinue EvalResult   | DidStep EvalResult   | DidExec EvalResult-  | GotStacktrace [StackFrame]+  | GotThreads [DebuggeeThread]+  | GotStacktrace [DbgStackFrame]   | GotScopes [ScopeInfo]   | GotVariables (Either VarInfo [VarInfo])   | Aborted String@@ -234,6 +239,16 @@   | ManyBreaksFound [BreakFound]   deriving (Show) +-- | A reference to a remote thread by remote id+-- See 'getRemoteThreadId'.+newtype RemoteThreadId = RemoteThreadId+    { remoteThreadIntRef :: Int+    -- ^ The number identifier of the thread on the (remote) interpreter. To+    -- find the proper remote 'ThreadId' corresponding to this numeric+    -- identifier, lookup the 'remoteThreadIntRef' in the 'ThreadMap'+    }+    deriving (Show, Eq, Ord)+ data EvalResult   = EvalCompleted { resultVal :: String                   , resultType :: String@@ -243,13 +258,26 @@                   -- that the user can expand as a normal variable.                   }   | EvalException { resultVal :: String, resultType :: String }-  | EvalStopped   { breakId :: Maybe GHC.InternalBreakpointId {-^ Did we stop at an exception (@Nothing@) or at a breakpoint (@Just@)? -} }+  | EvalStopped   { breakId :: Maybe GHC.InternalBreakpointId+                  -- ^ Did we stop at an exception (@Nothing@) or at a breakpoint (@Just@)?+                  , breakThread :: RemoteThreadId+                  -- ^ In which thread did we hit the breakpoint?+                  }   -- | Evaluation failed for some reason other than completed/completed-with-exception/stopped.   | EvalAbortedWith String   deriving (Show) -data StackFrame-  = StackFrame+data DebuggeeThread+  = DebuggeeThread+    { tId :: !RemoteThreadId+    -- ^ An identifier for a thread on the (possibly remote) debuggee process+    , tName :: !(Maybe String)+    -- ^ Thread label, if there is one+    }+    deriving (Show)++data DbgStackFrame+  = DbgStackFrame     { name :: String     -- ^ Title of stack frame     , sourceSpan :: SourceSpan
haskell-debugger/GHC/Debugger/Monad.hs view
@@ -18,9 +18,12 @@ import Data.Function import Data.IORef import Data.Maybe+import Data.Version (makeVersion, showVersion) import Prelude hiding (mod) import System.IO+#ifdef MIN_VERSION_unix import System.Posix.Signals+#endif import qualified Data.IntMap as IM import qualified Data.List as L import qualified Data.List.NonEmpty as NonEmpty@@ -46,6 +49,7 @@ import GHC.Types.SourceText import GHC.Types.Unique.Supply as GHC import GHC.Unit.Module.Graph+import GHC.Unit.State import GHC.Unit.Module.ModSummary as GHC import GHC.Unit.Types import GHC.Utils.Logger as GHC@@ -58,7 +62,9 @@ import GHC.Debugger.Runtime.Term.Key import GHC.Debugger.Session import GHC.Debugger.Session.Builtin+import GHC.Debugger.Runtime.Compile.Cache import qualified GHC.Debugger.Breakpoint.Map as BM+import qualified GHC.Debugger.Runtime.Thread.Map as TM  import {-# SOURCE #-} GHC.Debugger.Runtime.Instances.Discover (RuntimeInstancesCache, emptyRuntimeInstancesCache) @@ -87,12 +93,15 @@       -- The TermKeyMap map is a reverse lookup map to find which references       -- already exist for given names -      , termCache         :: IORef TermCache-      -- ^ TermCache-       , rtinstancesCache :: IORef RuntimeInstancesCache       -- ^ RuntimeInstancesCache +      , threadMap         :: IORef TM.ThreadMap+      -- ^ 'ThreadMap' for threads spawned by the debuggee++      , compCache         :: IORef CompCache+      -- ^ Cache loaded and compiled expressions.+       , genUniq           :: IORef Int       -- ^ Generates unique ints @@ -164,8 +173,10 @@ runDebugger l dbg_out rootDir compDir libdir units ghcInvocation' extraGhcArgs mainFp conf (Debugger action) = do   let ghcInvocation = filter (\case ('-':'B':_) -> False; _ -> True) ghcInvocation'   GHC.runGhc (Just libdir) $ do+#ifdef MIN_VERSION_unix     -- Workaround #4162     _ <- liftIO $ installHandler sigINT Default Nothing+#endif     dflags0 <- GHC.getSessionDynFlags     let dflags1 = dflags0           { GHC.ghcMode = GHC.CompManager@@ -175,7 +186,9 @@           , GHC.canUseErrorLinks = conf.supportsANSIHyperlinks           }           -- Default debugger settings+          `GHC.xopt_set` LangExt.TypeApplications           `GHC.xopt_set` LangExt.PackageImports+          `GHC.xopt_set` LangExt.MagicHash -- needed for some of the expressions we compile           `GHC.gopt_set` GHC.Opt_ImplicitImportQualified           `GHC.gopt_set` GHC.Opt_IgnoreOptimChanges           `GHC.gopt_set` GHC.Opt_IgnoreHpcChanges@@ -220,7 +233,7 @@      -- Setup base HomeUnitGraph     setupHomeUnitGraph (NonEmpty.toList flagsAndTargets)-    -- Downsweep user-given modules first +    -- Downsweep user-given modules first     mod_graph_base <- doDownsweep Nothing      if_cache <- Just <$> liftIO newIfaceCache@@ -403,6 +416,8 @@ -- See also comment on the @'hsDbgViewUnitId'@ field of @'DebuggerState'@ findHsDebuggerViewUnitId :: ModuleGraph -> GHC.Ghc (Maybe UnitId) findHsDebuggerViewUnitId mod_graph = do+  hsc_env <- getSession+  let unitState = hsc_units hsc_env    -- Only looks at unit-nodes, this is not robust!   -- TODO: Better lookup of unit-id@@ -410,12 +425,26 @@         [ uid         | UnitNode _deps uid <- mg_mss mod_graph         , "haskell-debugger-view" `L.isPrefixOf` unitIdString uid+            || "hskll-dbggr-vw" `L.isPrefixOf` unitIdString uid         ] +      -- If the haskell-debugger-view is in the dependency graph, it must have+      -- one of the versions the debugger is known to support:+      supported_versions+        = [ makeVersion [0, 2, 0, 0] ]+   case hskl_dbgr_vws of-    [hdv_uid] ->+    [hdv_uid] -> do       -- In transitive closure, use that one.-      return (Just hdv_uid)+      -- Check that the version is exactly 0.2.0.0+      case lookupUnit unitState (RealUnit (Definite hdv_uid)) of+        Just unitInfo -> do+          let version = unitPackageVersion unitInfo+          if version `elem` supported_versions+            then return (Just hdv_uid)+            else throwM UnsupportedHsDbgViewVersion{supportedVersions=supported_versions, actualVersion=version}+        Nothing ->+          error "Could not find unit info for haskell-debugger-view"     [] -> do       return Nothing     _  ->@@ -480,8 +509,9 @@ initialDebuggerState l hsDbgViewUid =   DebuggerState <$> liftIO (newIORef BM.empty)                 <*> liftIO (newIORef mempty)-                <*> liftIO (newIORef mempty)                 <*> liftIO (newIORef emptyRuntimeInstancesCache)+                <*> liftIO (newIORef TM.emptyThreadMap)+                <*> liftIO (newIORef emptyCompCache)                 <*> liftIO (newIORef 0)                 <*> pure hsDbgViewUid                 <*> pure l@@ -490,12 +520,22 @@ liftGhc :: GHC.Ghc a -> Debugger a liftGhc = Debugger . ReaderT . const +--------------------------------------------------------------------------------+ data DebuggerFailedToLoad = DebuggerFailedToLoad instance Exception DebuggerFailedToLoad instance Show DebuggerFailedToLoad where   show DebuggerFailedToLoad = "Failed to compile and load user project." ---------------------------------------------------------------------------------+data UnsupportedHsDbgViewVersion = UnsupportedHsDbgViewVersion+  { supportedVersions :: [ Version ]+  , actualVersion :: Version+  }+instance Exception UnsupportedHsDbgViewVersion+instance Show UnsupportedHsDbgViewVersion where+  show (UnsupportedHsDbgViewVersion supported actual) =+    "Cannot use unsupported haskell-debugger-view version found in the transitive closure: " ++ showVersion actual +++    " (supported: " ++ L.intercalate ", " (map showVersion supported) ++ ")"  -------------------------------------------------------------------------------- -- * Modules
+ haskell-debugger/GHC/Debugger/Run.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ViewPatterns #-}+module GHC.Debugger.Run where++import GHC.Utils.Outputable+import Control.Monad.IO.Class+import Control.Monad.Catch+import Control.Monad.Reader+import Data.IORef+import qualified Data.List as List+import Data.Maybe+import System.FilePath+import System.Directory+import qualified Prettyprinter as Pretty++import GHC+import GHC.Builtin.Names (gHC_INTERNAL_GHCI_HELPERS)+import GHC.Unit.Types+import GHC.Data.FastString+import GHC.Driver.DynFlags as GHC+import GHC.Driver.Monad as GHC+import GHC.Driver.Env as GHC+import GHC.Runtime.Debugger.Breakpoints as GHC+import qualified GHC.Unit.Module.ModSummary as GHC+import GHC.Types.Name.Occurrence (mkVarOccFS)+import GHC.Types.Name.Reader as RdrName (mkOrig)+import GHC.Utils.Outputable as GHC+import qualified GHCi.Message as GHCi+import qualified GHC.Data.Strict as Strict++import GHC.Debugger.Stopped.Variables+import GHC.Debugger.Monad+import GHC.Debugger.Utils+import GHC.Debugger.Interface.Messages+import GHC.Debugger.Logger as Logger+import qualified GHC.Debugger.Breakpoint.Map as BM+import GHC.Debugger.Runtime.Thread++data EvalLog+  = LogEvalModule GHC.Module++instance Pretty EvalLog where+  pretty = \ case+    LogEvalModule modl -> "Eval Module Context:" Pretty.<+> pretty (GHC.showSDocUnsafe (ppr modl))++--------------------------------------------------------------------------------+-- * Evaluation+--------------------------------------------------------------------------------++-- | Run a program with debugging enabled+debugExecution :: Recorder (WithSeverity EvalLog) -> FilePath -> EntryPoint -> [String] {-^ Args -} -> Debugger EvalResult+debugExecution recorder entryFile entry args = do+  -- consider always using :trace like ghci-dap to always have a stacktrace?+  -- better solution could involve profiling stack traces or from IPE info?+  modSummaryOfEntryFile <- findUnitIdOfEntryFile entryFile+  let modOfEntryFile = GHC.ms_mod modSummaryOfEntryFile+      unitIdOfEntryFile = GHC.ms_unitid modSummaryOfEntryFile++  let+    evalModule = mkModule (RealUnit (Definite unitIdOfEntryFile))+                                         (moduleName modOfEntryFile)++  logWith recorder Info $ LogEvalModule evalModule+  old_context <- GHC.getContext+  GHC.setContext [GHC.IIModule evalModule]++  (entryExp, exOpts) <- case entry of+    MainEntry nm -> do+      let prog = fromMaybe "main" nm+      -- the wrapper is equivalent to GHCi's `:main arg1 arg2 arg3`+      wrapper <- mkEvalWrapper prog args -- bit weird that the prog name is the expression but fine+      let execWrap' fhv = GHCi.EvalApp (GHCi.EvalThis wrapper) (GHCi.EvalThis fhv)+          opts = GHC.execOptions {execWrap = execWrap'}+      return (prog, opts)++    FunctionEntry fn ->+      -- TODO: if "args" is unescaped (e.g. "some", "thing"), then "some" and+      -- "thing" will be interpreted as variables. To pass strings it needs to+      -- be "\"some\"" "\"things\"".+      return (fn ++ " " ++ unwords args, GHC.execOptions)++  exec_res <- GHC.execStmt entryExp exOpts+  GHC.setContext old_context -- restore context after running `main`+  handleExecResult exec_res+  where+    -- It's not ideal to duplicate these two functions from ghci, but its unclear where they would better live. Perhaps next to compileParsedExprRemote? The issue is run+    mkEvalWrapper :: GhcMonad m => String -> [String] -> m ForeignHValue+    mkEvalWrapper progname' args' =+      runInternal $ GHC.compileParsedExprRemote+      $ evalWrapper' `GHC.mkHsApp` nlHsString progname'+                     `GHC.mkHsApp` nlList (map nlHsString args')+      where+        nlHsString = nlHsLit . mkHsString+        evalWrapper' =+          GHC.nlHsVar $ RdrName.mkOrig gHC_INTERNAL_GHCI_HELPERS (mkVarOccFS (fsLit "evalWrapper"))++    -- run internal here serves to overwrite certain flags while executing the+    -- internal "evalWrapper" computation which is not relevant to the user.+    runInternal :: GhcMonad m => m a -> m a+    runInternal =+        withTempSession mkTempSession+      where+        mkTempSession = hscUpdateFlags (\dflags -> dflags+          { -- Disable dumping of any data during evaluation of GHCi's internal expressions. (#17500)+            dumpFlags = mempty+          }+              -- We depend on -fimplicit-import-qualified to compile expr+              -- with fully qualified names without imports (gHC_INTERNAL_GHCI_HELPERS above).+              `gopt_set` Opt_ImplicitImportQualified+          )++    findUnitIdOfEntryFile :: GhcMonad m => FilePath -> m GHC.ModSummary+    findUnitIdOfEntryFile fp = do+      afp <- normalise <$> liftIO (makeAbsolute fp)+      modSums <- getAllLoadedModules+      let normalisedModLoc = fmap normalise . GHC.ml_hs_file . GHC.ms_location+      case List.find ((Just afp ==) . normalisedModLoc) modSums of+        Nothing -> error $ "findUnitIdOfEntryFile: no unit id found for: " ++ fp ++ "\nCandidates were:\n" ++ unlines (map (show . normalisedModLoc) modSums)+        Just summary -> pure summary++-- | Resume execution of the stopped debuggee program+doContinue :: Debugger EvalResult+doContinue = do+  leaveSuspendedState+  GHC.resumeExec RunToCompletion Nothing+    >>= handleExecResult++-- | Resume execution but only take a single step.+doSingleStep :: Debugger EvalResult+doSingleStep = do+  leaveSuspendedState+  GHC.resumeExec SingleStep Nothing+    >>= handleExecResult++doStepOut :: Debugger EvalResult+doStepOut = do+  leaveSuspendedState+  mb_span <- getCurrentBreakSpan+  case mb_span of+    Nothing ->+      GHC.resumeExec (GHC.StepOut Nothing) Nothing+        >>= handleExecResult+    Just loc -> do+      md <- fromMaybe (error "doStepOut") <$> getCurrentBreakModule+      ticks <- fromMaybe (error "doLocalStep:getTicks") <$> makeModuleLineMap md+      let current_toplevel_decl = enclosingTickSpan ticks loc+      GHC.resumeExec (GHC.StepOut (Just (RealSrcSpan current_toplevel_decl Strict.Nothing))) Nothing+        >>= handleExecResult++-- | Resume execution but stop at the next tick within the same function.+--+-- To do a local step, we get the SrcSpan of the current suspension state and+-- get its 'enclosingTickSpan' to use as a filter for breakpoints in the call+-- to 'resumeExec'. Execution will only stop at breakpoints whose span matches+-- this enclosing span.+doLocalStep :: Debugger EvalResult+doLocalStep = do+  leaveSuspendedState+  mb_span <- getCurrentBreakSpan+  case mb_span of+    Nothing -> error "not stopped at a breakpoint?!"+    Just (UnhelpfulSpan _) -> do+      liftIO $ putStrLn "Stopped at an exception. Forcing step into..."+      GHC.resumeExec SingleStep Nothing >>= handleExecResult+    Just loc -> do+      md <- fromMaybe (error "doLocalStep") <$> getCurrentBreakModule+      -- TODO: Cache moduleLineMap?+      ticks <- fromMaybe (error "doLocalStep:getTicks") <$> makeModuleLineMap md+      let current_toplevel_decl = enclosingTickSpan ticks loc+      GHC.resumeExec (LocalStep (RealSrcSpan current_toplevel_decl mempty)) Nothing >>= handleExecResult++-- | Evaluate expression. Includes context of breakpoint if stopped at one (the current interactive context).+doEval :: String -> Debugger EvalResult+doEval expr = do+  excr <- (Right <$> GHC.execStmt expr GHC.execOptions) `catch` \(e::SomeException) -> pure (Left (displayException e))+  case excr of+    Left err -> pure $ EvalAbortedWith err+    Right ExecBreak{} -> continueToCompletion >>= handleExecResult+    Right r@ExecComplete{} -> handleExecResult r++-- | Resume execution with single step mode 'RunToCompletion', skipping all breakpoints we hit, until we reach 'ExecComplete'.+--+-- We use this in 'doEval' because we want to ignore breakpoints in expressions given at the prompt.+continueToCompletion :: Debugger GHC.ExecResult+continueToCompletion = do+  execr <- GHC.resumeExec GHC.RunToCompletion Nothing+  case execr of+    GHC.ExecBreak{} -> continueToCompletion+    GHC.ExecComplete{} -> return execr++-- | Turn a GHC's 'ExecResult' into an 'EvalResult' response+handleExecResult :: GHC.ExecResult -> Debugger EvalResult+handleExecResult = \case+    ExecComplete {execResult} -> do+      case execResult of+        Left e -> return (EvalException (show e) "SomeException")+        Right [] -> return (EvalCompleted "" "" NoVariables) -- Evaluation completed without binding any result.+        Right (n:_ns) -> inspectName n >>= \case+          Just VarInfo{varValue, varType, varRef} -> do+            return (EvalCompleted varValue varType varRef)+          Nothing     -> liftIO $ fail "doEval failed"+    ExecBreak {breakNames = _, breakPointId = Nothing} -> do+      -- Stopped at an exception+      -- TODO: force the exception to display string with Backtrace?+      rt_id <- getRemoteThreadIdFromContext+      return EvalStopped{ breakId = Nothing+                        , breakThread = rt_id }+    ExecBreak {breakNames = _, breakPointId = Just bid} -> do+      bm <- liftIO . readIORef =<< asks activeBreakpoints+      case BM.lookup bid bm of+        -- todo: BreakpointAfterCountCond is not handled yet.+        Just (BreakpointWhenCond cond, _) -> do+          let evalFailedMsg e = text $ "Evaluation of conditional breakpoint expression failed with " ++ e ++ "\nIgnoring..."+          let resume = GHC.resumeExec GHC.RunToCompletion Nothing >>= handleExecResult+          doEval cond >>= \case+            EvalStopped{} -> error "impossible for doEval"+            EvalCompleted { resultVal, resultType } ->+              if resultType == "Bool" then do+                if resultVal == "True" then do+                  rt_id <- getRemoteThreadIdFromContext+                  return EvalStopped{ breakId = Just bid+                                    , breakThread = rt_id }+                else+                  resume+              else do+                logSDoc Logger.Warning (evalFailedMsg "\"expression resultType is != Bool\"")+                resume+            EvalException { resultVal } -> do+              logSDoc Logger.Warning (evalFailedMsg resultVal)+              resume+            EvalAbortedWith e -> do+              logSDoc Logger.Warning (evalFailedMsg e)+              resume++        -- Unconditionally 'EvalStopped' in all other cases+        _ -> do+          rt_id <- getRemoteThreadIdFromContext+          return EvalStopped{ breakId = Just bid+                            , breakThread = rt_id }++-- | Get the value and type of a given 'Name' as rendered strings in 'VarInfo'.+inspectName :: Name -> Debugger (Maybe VarInfo)+inspectName n = do+  GHC.lookupName n >>= \case+    Nothing -> do+      liftIO . putStrLn =<< display (text "Failed to lookup name: " <+> ppr n)+      pure Nothing+    Just tt -> Just <$> tyThingToVarInfo tt++getRemoteThreadIdFromContext :: Debugger RemoteThreadId+getRemoteThreadIdFromContext = do+  GHC.getResumeContext >>= \case+    resume1:_ ->+      getRemoteThreadIdFromRemoteContext $ GHC.resumeContext resume1+    _ -> error "No resumes but stopped?!?"
haskell-debugger/GHC/Debugger/Runtime.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE OrPatterns, GADTs, LambdaCase, NamedFieldPuns, TemplateHaskellQuotes #-} module GHC.Debugger.Runtime where -import Data.IORef import Control.Monad.Reader import qualified Data.List as L @@ -12,7 +11,6 @@ import GHC.Runtime.Heap.Inspect  import GHC.Debugger.Runtime.Term.Key-import GHC.Debugger.Runtime.Term.Cache import GHC.Debugger.Monad  -- | Obtain the runtime 'Term' from a 'TermKey'.@@ -23,39 +21,27 @@ obtainTerm :: TermKey -> Debugger Term obtainTerm key = do   hsc_env <- getSession-  tc_ref  <- asks termCache-  tc      <- liftIO $ readIORef tc_ref-  case lookupTermCache key tc of-    -- cache miss: reconstruct, then store.-    Nothing ->-      let-        -- Recursively get terms until we hit the desired key.-        getTerm = \case-          FromId i -> GHC.obtainTermFromId defaultDepth False{-don't force-} i-          FromPath k pf -> do-            term <- obtainTerm k-            liftIO $ expandTerm hsc_env $ case term of-              Term{dc=Right dc, subTerms} -> case pf of-                PositionalIndex ix -> subTerms !! (ix-1)-                LabeledField fl    ->-                  case L.findIndex (== fl) (map flSelector $ dataConFieldLabels dc) of-                    Just ix -> subTerms !! ix-                    Nothing -> error "Couldn't find labeled field in dataConFieldLabels"-              NewtypeWrap{wrapped_term} ->-                wrapped_term -- regardless of PathFragment-              RefWrap{wrapped_term} ->-                wrapped_term -- regardless of PathFragment-              _ -> error ("Unexpected term for the given TermKey because <term> should have been expanded before and we're getting a path fragment!\n" ++ showPprUnsafe (ppr key <+> ppr k <+> ppr pf))-          FromCustomTerm _key _name ctm -> do-            -- For custom terms return them straightaway.-            liftIO $ expandTerm hsc_env ctm-       in do-        term <- getTerm key-        liftIO $ modifyIORef tc_ref (insertTermCache key term)-        return term+   -- Recursively get terms until we hit the desired key.+  case key of+     FromId i -> GHC.obtainTermFromId defaultDepth False{-don't force-} i+     FromPath k pf -> do+       term <- obtainTerm k+       liftIO $ expandTerm hsc_env $ case term of+         Term{dc=Right dc, subTerms} -> case pf of+           PositionalIndex ix -> subTerms !! (ix-1)+           LabeledField fl    ->+             case L.findIndex (== fl) (map flSelector $ dataConFieldLabels dc) of+               Just ix -> subTerms !! ix+               Nothing -> error "Couldn't find labeled field in dataConFieldLabels"+         NewtypeWrap{wrapped_term} ->+           wrapped_term -- regardless of PathFragment+         RefWrap{wrapped_term} ->+           wrapped_term -- regardless of PathFragment+         _ -> error ("Unexpected term for the given TermKey because <term> should have been expanded before and we're getting a path fragment!\n" ++ showPprUnsafe (ppr key <+> ppr k <+> ppr pf))+     FromCustomTerm _key _name ctm -> do+       -- For custom terms return them straightaway.+       liftIO $ expandTerm hsc_env ctm -    -- cache hit-    Just hit -> return hit  -- | Before returning a 'Term' we want to expand its heap representation up to the 'defaultDepth' --
+ haskell-debugger/GHC/Debugger/Runtime/Compile.hs view
@@ -0,0 +1,65 @@+-- | A interface to compiling and loading expressions/values/variables into the+-- debuggee runtime.+--+-- You should prefer this interface to the GHC's 'compileExprRemote' whenever+-- the loaded expression would benefit from being cached and re-used, to avoid+-- recompilation and loading.+--+-- Even better, you should **always prefer** to use the 'RemoteExpr' abstraction+-- for constructing and evaluating expressions on the remote debuggee process.+-- In it, there is finer grained caching and a good interface for constructing+-- (possibly complex) expressions.+module GHC.Debugger.Runtime.Compile+  ( compileVar+  , compileRaw+  )+  where++import Control.Monad.Reader+import Data.IORef+import GHC++import GHC.Debugger.Monad+import GHC.Debugger.Utils+import GHC.Debugger.Runtime.Compile.Cache+import GHC.Debugger.Logger as Logger++-- | Compile and load a fully qualified external variable, potentially applied+-- to a few type arguments (i.e. by specializing it). E.g. @"GHC.Base" "pure" ["IO"]+--+-- The loaded (specialized) expression is cached.+compileVar :: ModuleName -> String -> [String] -> Debugger ForeignHValue+compileVar mod_name var_name ty_args =+  let raw_expr =+        ("(" ++ moduleNameString mod_name+             ++ "." ++ var_name ++ ") "+             ++ unwords (map ('@':) ty_args))+   in compileRaw raw_expr++-- | Compile and load a raw expression string.+--+-- Note: it is easy to wrongly cache arbitrary compilation strings. You+-- shouldn't use 'copmileRaw' lightly since these arbitrary strings typically+-- are not very cacheable (there won't be many if any hits).+--+-- If you need an expression more complex than a single variable specialized at+-- a few types, or even then, you should just prefer to use @'RemoteExpr'@ to+-- describe and load that remote expression. 'RemoteExpr' will cache the+-- relevant bits (loading of external names) at the right granularity while not+-- caching, say, chains of function applications, which can be trivially+-- executed on the remote process in a single go without any compilation.+--+-- User beware!+compileRaw :: String -> Debugger ForeignHValue+compileRaw raw_expr = do+  compCacheRef <- asks compCache+  compCacheVal <- liftIO $ readIORef compCacheRef++  case lookupCompRaw raw_expr compCacheVal of+    Just fhv -> do+      logSDoc Logger.Debug (text "Cache hit! on" <+> text raw_expr)+      return fhv+    Nothing  -> do+      fhv <- compileExprRemote raw_expr+      liftIO $ modifyIORef' compCacheRef (insertCompRaw raw_expr fhv)+      return fhv
+ haskell-debugger/GHC/Debugger/Runtime/Compile/Cache.hs view
@@ -0,0 +1,35 @@+-- | A compilation cache which allows us to re-use an already loaded+-- expression/variable rather than recompiling it from scratch.+module GHC.Debugger.Runtime.Compile.Cache+  ( CompCache+  , insertCompRaw+  , lookupCompRaw+  , emptyCompCache+  ) where++import GHC++import qualified Data.Map as M++-- | Mapping from compile expressions (as strings) to their @ForeignHValue@s+newtype CompCache = CompCache (M.Map String ForeignHValue)++-- | Store the result of compiling and loading a raw expression string.+--+-- Note: it is easy to wrongly cache arbitrary compilation strings. You+-- shouldn't do this lightly since these arbitrary strings typically are not+-- very cacheable (there won't be many if any hits).+--+-- **Instead, one should always prefer to use @'RemoteExpr'@ to describe and load+-- remote expression, which will be implicitly cached but at a much finer+-- grained level.**+insertCompRaw :: String -> ForeignHValue -> CompCache -> CompCache+insertCompRaw s v (CompCache m) = CompCache (M.insert s v m)++-- | Look if the raw string value of an expression has already been compiled and loaded+lookupCompRaw :: String -> CompCache -> Maybe ForeignHValue+lookupCompRaw s (CompCache m) = M.lookup s m++-- | Nothing in it+emptyCompCache :: CompCache+emptyCompCache = CompCache M.empty
+ haskell-debugger/GHC/Debugger/Runtime/Eval.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE LambdaCase, DataKinds, TypeFamilies #-}++-- | Higher-level interface to evaluating things in the (possibly remote) debuggee process+module GHC.Debugger.Runtime.Eval+  ( evalExpr+  , handleSingStatus+  , BadEvalStatus(..)+  ) where++import GHC+import GHC.Runtime.Interpreter as Interp+import GHCi.RemoteTypes+import GHCi.Message+import Control.Exception+import GHC.Driver.Env+import GHC.Driver.Config+import Control.Monad.IO.Class++import GHC.Debugger.Utils+import GHC.Debugger.Monad+import GHC.Debugger.Logger as Logger++-- * Evaluating things on debuggee ---------------------------------------------++-- | Evaluate a raw 'EvalExpr' which represents a debuggee expression of type @IO [a]@+evalExpr :: EvalExpr ForeignHValue -> Debugger (Either BadEvalStatus [ForeignHValue])+evalExpr eval_expr = do+  logSDoc Logger.Debug (text "evalExpr:" <+> (text (show (mkUnit eval_expr))))+  hsc_env <- getSession+  let eval_opts = initEvalOpts (hsc_dflags hsc_env) EvalStepNone+  handleMultiStatus <$>+    liftIO (evalStmt (hscInterp hsc_env) eval_opts eval_expr)+  where+    mkUnit :: EvalExpr a -> EvalExpr ()+    mkUnit EvalThis{} = EvalThis ()+    mkUnit (EvalApp a b) = mkUnit a `EvalApp` mkUnit b++-- ** Handling evaluation results ----------------------------------------------++-- | Handle the 'EvalStatus_' of an evaluation using 'EvalStepNone' which returns a single value+handleSingStatus :: Either BadEvalStatus [ForeignHValue] -> Either BadEvalStatus ForeignHValue+handleSingStatus status =+  case status of+    Right [sing]  -> Right sing+    Right []      -> Left EvalReturnedNoResults+    Right (_:_:_) -> Left EvalReturnedTooManyResults+    Left e -> Left e++-- | Handle the 'EvalStatus_' of an evaluation using 'EvalStepNone' which returns a list of values+handleMultiStatus :: EvalStatus_ [ForeignHValue] [HValueRef] -> Either BadEvalStatus [ForeignHValue]+handleMultiStatus status =+  case status of+    EvalComplete _ (EvalSuccess res) -> Right res+    EvalComplete _ (EvalException e) ->+      Left (EvalRaisedException (fromSerializableException e))+    EvalBreak {} ->+      --TODO: Could we accidentally hit this if we set a breakpoint regardless of whether EvalStep=None? perhaps.+      Left EvalHitUnexpectedBreakpoint++--------------------------------------------------------------------------------+-- * Exceptions+--------------------------------------------------------------------------------++data BadEvalStatus+  = EvalRaisedException SomeException+  | EvalHitUnexpectedBreakpoint+  | EvalReturnedNoResults+  | EvalReturnedTooManyResults+  deriving Show++instance Exception BadEvalStatus
+ haskell-debugger/GHC/Debugger/Runtime/Eval/RemoteExpr.hs view
@@ -0,0 +1,274 @@+{-# LANGUAGE LambdaCase, GADTs, DataKinds, MagicHash, StandaloneKindSignatures #-}+-- | A DSL for evaluating remote expressions on the (possibly) remote debuggee process+--+-- Meant to be imported qualified @as Remote@ for use with @QualifiedDo@.+--+-- @+-- import GHC.Debugger.Runtime.Eval.RemoteExpr (RemoteExpr)+-- import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote+-- @+module GHC.Debugger.Runtime.Eval.RemoteExpr+  (+  -- * Building remote expressions+    RemoteExpr+  , var, ref, untypedRef+  , lit, raw+  , app, appRef+  , withUnboxed+  , fmap, (<$>)+  , pure, return+  , (>>=), (>>)++  -- * Evaluating remote expressions+  , eval, evalIO, evalString, evalIOList, evalIOString+  ) where++import Prelude hiding (pure, return, (>>=), (>>), fmap, (<$>))+import qualified Prelude+import qualified Data.Kind as Kind+import Control.Monad.Except+import GHC.Exts+import GHC+import GHC.Driver.Env+import GHC.Runtime.Interpreter as Interp hiding (evalIO, evalString)+import qualified GHC.Runtime.Interpreter as Interp+import Control.Monad.Reader+import GHCi.RemoteTypes++import GHC.Debugger.Logger as Logger+import GHC.Debugger.Monad+import GHC.Debugger.Utils+import GHC.Debugger.Runtime.Eval as Raw+import GHC.Debugger.Runtime.Compile++--------------------------------------------------------------------------------+-- * Higher-level: eDSL for (possibly) remote evaluation on the debuggee+--------------------------------------------------------------------------------++-- | A remote expression to be evaluated on the debuggee process.+-- Note: the remote expression must have a boxed representation type.+type RemoteExpr :: forall l. TYPE (BoxedRep l) -> Kind.Type+data RemoteExpr t where++  -- | Parse, compile, and load a raw expression string onto the remote process.+  -- This is a low-level escape hatch; prefer using the other constructors.+  -- The loaded expression is cached by its string.+  RemRaw    :: String -> RemoteExpr a++  -- | A top-level or in-another-module name (aka external name) in the debuggee process.+  -- The list of strings is the list of (fully-qualified) types the expression+  -- should be applied to.+  RemVar    :: -- forall {l} (a :: TYPE (BoxedRep l))+             ModuleName -> String -> [String] -> RemoteExpr a++  -- | A reference to a value in the debuggee heap+  RemRef    :: forall {l} (a :: TYPE (BoxedRep l))+             . ForeignHValue -> RemoteExpr a++  -- | An Int literal (@Int@)+  RemInt    :: Int -> RemoteExpr Int++  -- | Apply a remote function to a remote argument to get a remote result+  --+  -- Note: the result type @b@ must have a BoxedRep!+  RemApp    :: forall {l} (a :: TYPE (BoxedRep l)) b+             . RemoteExpr (a -> b) -> RemoteExpr a -> RemoteExpr b++  -- | IO monadic bind on the remote process+  RemBindIO :: RemoteExpr (IO a) -> (RemoteExpr a -> RemoteExpr (IO b)) -> RemoteExpr (IO b)++-- | Apply a remote function to a remote argument to get a remote result+var :: -- forall {l} (a :: TYPE (BoxedRep l))+     ModuleName -> String -> [String] -> RemoteExpr a+var = RemVar++-- | A reference to a value in the debuggee heap+ref :: ForeignRef a -> RemoteExpr a+ref = RemRef . castForeignRef++-- | A reference to a value in the debuggee heap+untypedRef :: forall {l} (a :: TYPE (BoxedRep l))+            . ForeignHValue -> RemoteExpr a+untypedRef = RemRef++-- | A literal unboxed int+lit :: Int -> RemoteExpr Int+lit = RemInt++-- | A raw expression string to be parsed, compiled, and loaded onto the remote process+raw :: String -> RemoteExpr a+raw = RemRaw++-- | Apply a remote function to a remote argument to get a remote result+app :: forall {l} (a :: TYPE (BoxedRep l)) b+     . RemoteExpr (a -> b) -> RemoteExpr a -> RemoteExpr b+app = RemApp++-- | Apply a remote function to a remote 'ForeignRef' to get a remote result+appRef :: RemoteExpr (a -> b) -> ForeignRef a -> RemoteExpr b+appRef rf = RemApp rf . ref++-- | Apply a remote function after unboxing a remote int argument+-- (We need to unbox the Int on the debuggee side)+withUnboxed :: RemoteExpr Int -> (RemoteExpr (Int# -> b)) -> RemoteExpr b+withUnboxed i f = (RemRaw "\\f i -> case i of GHC.Exts.I# i# -> f i#")+                    `RemApp` f `RemApp` i++-- | IO fmap on the remote process+fmap :: (RemoteExpr a -> RemoteExpr b) -> (RemoteExpr (IO a) -> RemoteExpr (IO b))+fmap f io_x = io_x >>= \x -> pure (f x)++-- | IO fmap on the remote process+(<$>) :: (RemoteExpr a -> RemoteExpr b) -> (RemoteExpr (IO a) -> RemoteExpr (IO b))+(<$>) = fmap++-- | IO pure on the remote process+pure :: RemoteExpr a -> RemoteExpr (IO a)+pure = RemApp (var (mkModuleName "GHC.Base") "pure" ["IO"])++-- | IO return on the remote process+return :: RemoteExpr a -> RemoteExpr (IO a)+return = pure++-- | IO monadic bind on the remote process+(>>=) :: RemoteExpr (IO a) -> (RemoteExpr a -> RemoteExpr (IO b)) -> RemoteExpr (IO b)+(>>=) = RemBindIO++-- | IO monadic bind on the remote process+(>>) :: RemoteExpr (IO a) -> RemoteExpr (IO b) -> RemoteExpr (IO b)+(>>) ma mb = app (app andThen ma) mb+  where+    andThen :: RemoteExpr (IO a -> IO b -> IO b)+    andThen = var (mkModuleName "GHC.Base") ">>" ["IO"]++--------------------------------------------------------------------------------+-- * Evaluation of 'RemoteExprs' (higher level)+--------------------------------------------------------------------------------++-- | Evaluate a @a@ expression on the remote process and return the foreign+-- reference to the result.+eval :: RemoteExpr a -> Debugger (Either BadEvalStatus (ForeignRef a))+eval expr = evalIO (pure expr)++-- | Run an @IO a@ computation in the remote process and return the foreign+-- reference to the returned @a@+evalIO :: RemoteExpr (IO a) -> Debugger (Either BadEvalStatus (ForeignRef a))+evalIO expr = do+  r <- evalIOList (fmap singletonList expr)+  case r of+    Left e    -> Prelude.return (Left e)+    Right [x] -> Prelude.return (Right x)+    Right []  -> Prelude.return (Left EvalReturnedNoResults)+    Right _   -> Prelude.return (Left EvalReturnedTooManyResults)++-- | Evaluate a string expression on the remote process and return the string+-- to the debugger+evalString :: RemoteExpr String -> Debugger (Either BadEvalStatus String)+evalString expr = evalIOString (pure expr)++{- |+Evaluate a 'RemoteExpr' for a remote @IO [a]@ and return a list of+'ForeignHValue' with one element per returned @a@.++=== __Example__++@+Remote.evalIOList $ Remote.do+  clonedStack <- Remote.cloneThreadStack `Remote.appRef` threadIdRef+  frames      <- Remote.decodeStack      `Remote.app`    clonedStack+  return (Remote.ssc_stack `Remote.app` frames)+@+-}+evalIOList :: RemoteExpr (IO [a]) -> Debugger (Either BadEvalStatus [ForeignRef a])+evalIOList expr = runExceptT $ do+  lift $ logSDoc Logger.Debug (text "evalIOList" <+> text (show expr))++  res_fv <- debuggeeEval expr++  r <- lift $ Raw.evalExpr (EvalThis (castForeignRef res_fv))+  liftEither (map castForeignRef Prelude.<$> r)++-- | Execute an @IO String@ on the remote process and serialize the string back to the debugger.+evalIOString :: RemoteExpr (IO String) -> Debugger (Either BadEvalStatus String)+evalIOString expr = runExceptT $ do+  lift $ logSDoc Logger.Debug (text "evalIOString" <+> text (show expr))+  res_fv <- debuggeeEval expr+  hsc_env <- lift getSession+  liftIO (Interp.evalString (hscInterp hsc_env) (castForeignRef res_fv))++--------------------------------------------------------------------------------+-- ** Recursive evaluation of 'RemoteExpr' (lower-level)+--------------------------------------------------------------------------------++-- | Get the foreign reference to a heap value of type @a@ in the debuggee+-- process from the given remote expr.+--+-- The result can't be (@ForeignRef a@) because of levity polymorphism, so we+-- return the untyped foreign ref.+debuggeeEval :: RemoteExpr a -> ExceptT BadEvalStatus Debugger (ForeignRef a)+debuggeeEval expr = do+    eval_expr <- go (pure (singletonList expr))+    r <- lift $ handleSingStatus Prelude.<$> Raw.evalExpr eval_expr+    liftEither (castForeignRef Prelude.<$> r)+  where++    -- Construct the largest possible EvalExpr and then evaluate it all at once.+    -- When we find an IO action we execute it.+    go :: forall {l'} (a' :: TYPE (BoxedRep l'))+        . RemoteExpr a' -> ExceptT BadEvalStatus Debugger (EvalExpr ForeignHValue)+    go = \case+      RemRaw s -> do+        fhv <- lift $ compileRaw s+        Prelude.return (EvalThis fhv)+      RemVar mod_name var_name ty_args -> do+        fhv <- lift $ compileVar mod_name var_name ty_args+        Prelude.return (EvalThis fhv)+      RemRef r -> Prelude.return (EvalThis r)+      RemInt i ->+        -- todo: interpreter message for unboxed literals+        -- TODO: lookup in the cache if we loaded this int already.+        EvalThis Prelude.<$> lift (compileRaw (show i ++ ":: Int"))+      RemApp f arg -> do+        arg_e <- go arg+        f_e   <- go f+        Prelude.return (f_e `EvalApp` arg_e)+      RemBindIO iox k -> do++        expr_arg_io_fv <- go (fmapIO singletonListVar iox)++        e_arg_fv <- lift $ handleSingStatus Prelude.<$> Raw.evalExpr expr_arg_io_fv+        arg_fv   <- liftEither (castForeignRef Prelude.<$> e_arg_fv)+        go (k (ref arg_fv))++instance Show (RemoteExpr (a :: TYPE (BoxedRep l))) where+  show (RemRaw s) = "(" ++ s ++ ")"+  show (RemVar mod_name var_name ty_args) =+    "(" ++ moduleNameString mod_name ++ "." ++ var_name ++ ")"+        ++ (if null ty_args then "" else " ")+        ++ unwords (map ('@':) ty_args)+  show (RemRef _) = "<foreign ref>"+  show (RemInt i) = show i+  show (RemApp f arg) =+    "(" ++ show f ++ " " ++ show arg ++ ")"+  show (RemBindIO iox k) =+    let k_str = k (var (mkModuleName "Dummy") "dummy" [])+     in show iox ++ ">>= (\\dummy -> " ++ show k_str ++ ")"++--------------------------------------------------------------------------------+-- ** Builtins that are needed here too.++-- | Remote 'Data.List.singleton' (applied)+singletonList :: RemoteExpr a -> RemoteExpr [a]+singletonList = app singletonListVar++-- | Remote 'Data.List.singleton'+singletonListVar :: RemoteExpr (a -> [a])+singletonListVar = var (mkModuleName "Data.List") "singleton" []++-- | Remote 'fmap' for IO. Only works with @RemoteExpr (a -> b)@, not+-- @RemoteExpr a -> RemoteExpr b@ (unlike 'fmap').+--+-- We need it to avoid defining the evaluator for @RemBindIO@ in terms of+-- itself.+fmapIO :: RemoteExpr (a -> b) -> RemoteExpr (IO a) -> RemoteExpr (IO b)+fmapIO = app . app (var (mkModuleName "Data.Functor") "fmap" ["IO"])
+ haskell-debugger/GHC/Debugger/Runtime/Eval/RemoteExpr/Builtin.hs view
@@ -0,0 +1,82 @@+{-# LANGUAGE MagicHash #-}++-- | A module providing various remote external variables which are available+-- by default in any session.+--+-- A good way to check whether some function or variable can be added here is+-- if you can successfully ask for the type of its fully qualified name on @ghci@+-- (e.g. @:t Data.List.singleton@)+--+-- Meant to be imported qualified @as Remote@:+-- @+-- import GHC.Debugger.Runtime.Eval.RemoteExpr (RemoteExpr)+-- import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote+-- import qualified GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin as Remote+-- @+module GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin where++import GHC.Exts+import Data.Word+import Foreign.C.String+import GHC.Conc.Sync+import GHC.Unit.Module+import GHC.Stack.CloneStack+import GHC.Exts.Heap+import GHC.Exts.Heap.Closures+import GHC.Debugger.Runtime.Eval.RemoteExpr (RemoteExpr)+import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote++-- | Remote 'GHC.Stack.CloneStack.cloneThreadStack'+cloneThreadStack :: RemoteExpr ThreadId -> RemoteExpr (IO StackSnapshot)+cloneThreadStack = Remote.app $ Remote.var (mkModuleName "GHC.Stack.CloneStack") "cloneThreadStack" []++-- | Remote 'GHC.Stack.CloneStack.decode'+decode :: RemoteExpr StackSnapshot -> RemoteExpr (IO [StackEntry])+decode = Remote.app $ Remote.var (mkModuleName "GHC.Stack.CloneStack") "decode" []++-- | Remote 'GHC.Exts.Stack.decodeStack'+decodeStack :: RemoteExpr StackSnapshot -> RemoteExpr (IO StgStackClosure)+decodeStack = Remote.app $ Remote.var (mkModuleName "GHC.Exts.Stack") "decodeStack" []++-- | Remote 'GHC.Exts.Heap.Closures.ssc_stack'+ssc_stack :: RemoteExpr StgStackClosure -> RemoteExpr [StackFrame]+ssc_stack = Remote.app $ Remote.var (mkModuleName "GHC.Exts.Heap.Closures") "ssc_stack" []++-- | Remote 'GHC.Exts.Heap.getClosureData'+getClosureData :: RemoteExpr StgStackClosure -> RemoteExpr (IO Closure)+getClosureData = Remote.app $ Remote.var (mkModuleName "GHC.Exts.Heap") "getClosureData" ["GHC.Exts.LiftedRep", "_"]++-- | Remote 'GHC.Conc.Sync.fromThreadId'+fromThreadId :: RemoteExpr ThreadId -> RemoteExpr Word64+fromThreadId = Remote.app $ Remote.var (mkModuleName "GHC.Conc.Sync") "fromThreadId" []++-- | Remote 'GHC.Conc.Sync.threadStatus'+threadStatus :: RemoteExpr ThreadId -> RemoteExpr (IO ThreadStatus)+threadStatus = Remote.app $ Remote.var (mkModuleName "GHC.Conc.Sync") "threadStatus" []++-- | Remote 'GHC.Conc.Sync.listThreads'+listThreads :: RemoteExpr (IO [ThreadId])+listThreads = Remote.var (mkModuleName "GHC.Conc.Sync") "listThreads" []++-- | Remote 'GHC.Conc.Sync.threadLabel'+threadLabel :: RemoteExpr ThreadId -> RemoteExpr (IO (Maybe String))+threadLabel = Remote.app $ Remote.var (mkModuleName "GHC.Conc.Sync") "threadLabel" []++-- | Remote 'Foreign.C.String.peekCString'+peekCString :: RemoteExpr CString -> RemoteExpr (IO String)+peekCString = Remote.app $ Remote.var (mkModuleName "Foreign.C.String") "peekCString" []++-- | Remote 'GHC.Base.indexAddrArray#'+indexAddrArray :: RemoteExpr ByteArray# -> RemoteExpr (Int# -> Ptr a)+indexAddrArray = Remote.app $+  Remote.raw "\\b i -> GHC.Ptr.Ptr (GHC.Base.indexAddrArray# b i)"++-- | Remote 'Data.Maybe.maybeToList'+maybeToList :: RemoteExpr (Maybe a) -> RemoteExpr [a]+maybeToList = Remote.app $ Remote.var (mkModuleName "Data.Maybe") "maybeToList" []++-- | Function composition on the remote process+compose :: RemoteExpr (b -> c) -> RemoteExpr (a -> b) -> RemoteExpr (a -> c)+f `compose` g = Remote.app (Remote.app composeVar f) g where+  composeVar :: RemoteExpr ((b -> c) -> (a -> b) -> (a -> c))+  composeVar = Remote.var (mkModuleName "GHC.Base") "." []
haskell-debugger/GHC/Debugger/Runtime/Instances.hs view
@@ -1,29 +1,29 @@-{-# LANGUAGE TemplateHaskell, LambdaCase, BlockArguments #-}+{-# LANGUAGE TemplateHaskell, LambdaCase, BlockArguments, OrPatterns #-} module GHC.Debugger.Runtime.Instances where -import Control.Monad-import Control.Monad.Reader- import GHC import GHC.Driver.Env-import GHC.Plugins import GHC.Runtime.Eval import GHC.Runtime.Heap.Inspect import GHC.Runtime.Interpreter as Interp+import GHC.Utils.Outputable+import Control.Monad.Reader  import GHC.Debugger.Monad-import GHC.Debugger.View.Class-+import GHC.Debugger.Logger as Logger import GHC.Debugger.Runtime.Instances.Discover+import GHC.Debugger.Runtime.Term.Parser +data VarValueResult = VarValueResult { varValueResult :: String, varValueResultExpandable :: Bool }+ -------------------------------------------------------------------------------- -- * High level interface for 'DebugView' on 'Term's --------------------------------------------------------------------------------  -- | Get the custom representation of this 'Term' by applying a 'DebugView' -- instance 'debugValue' method if there is one.-debugValueTerm :: Term -> Debugger (Maybe VarValue)-debugValueTerm term = do+debugValueTerm :: Term -> Debugger (Maybe VarValueResult)+debugValueTerm term@(Suspension{} ; Term{}) = do   hsc_env <- getSession   let interp = hscInterp hsc_env   let ty = termType term@@ -38,33 +38,28 @@             return Nothing           Right transformed_v -> do -            liftIO (cvObtainTerm hsc_env maxBound True varValueIOTy transformed_v) >>= \case+            obtainParsedTerm "VarValue" maxBound True varValueIOTy transformed_v varValueParser >>= \case+              Left _ ->+                return Nothing+              Right (strTerm, valBool) -> do+                case strTerm of+                  (Suspension{} ; Term{}) -> do+                    valStr <- liftIO $+                      evalString interp (val strTerm {- whose type is IO String, from varValueIO -}) -              -- Get the Term of the VarValue to decode fields-              Term{ ty=_{-assert==VarValueIO-}-                  , subTerms=[strTerm, boolTerm]-                  } -> do+                    return $ Just VarValueResult+                      { varValueResult = valStr+                      , varValueResultExpandable = valBool+                      }+                  _ -> do+                    logSDoc Logger.Warning (text "debugValueTerm(2): Expecting" <+> ppr strTerm <+> text "to be a Term or Suspension.")+                    return Nothing+debugValueTerm term = do+  logSDoc Logger.Warning (text "debugValueTerm: Expecting" <+> ppr term <+> text "to be a Term or Suspension.")+  return Nothing -                valStr <- liftIO $-                  evalString interp (val strTerm {- whose type is IO String, from varValueIO -}) -                let valBool = case boolTerm of-                      Term{dc=Left "False"} -> False-                      Term{dc=Left "True"}  -> True-                      Term{dc=Right dc}-                        | falseDataCon == dc -> False-                      Term{dc=Right dc}-                        | trueDataCon == dc -> True-                      _ -> error "Decoding of VarValue failed" -                return $ Just VarValue-                  { varValue = valStr-                  , varExpandable = valBool-                  }-              _ ->-                -- Unexpected; the Term of VarValue should always be Term.-                return Nothing- -- | Get the custom representation of this 'Term' by applying a 'DebugView' -- instance 'debugFields' method if there is one. --@@ -73,9 +68,7 @@ -- -- Returns @Nothing@ if no instance was found for the type of the given term debugFieldsTerm :: Term -> Debugger (Maybe [(String, Term)])-debugFieldsTerm term = do-  hsc_env <- getSession-  let interp = hscInterp hsc_env+debugFieldsTerm term@(Suspension{} ; Term{}) = do   let ty = termType term   mbInst <- getDebugViewInstance ty   case mbInst of@@ -88,46 +81,9 @@             return Nothing           Right transformed_v -> do -            liftIO (cvObtainTerm hsc_env 2 True varFieldsIOTy transformed_v) >>= \case--              -- Get the Term of the VarFieldsIO-              NewtypeWrap-                { wrapped_term=fieldsListTerm-                } -> do--                fieldsTerms <- listTermToTermsList fieldsListTerm--                -- Process each term for the instance fields-                Just <$> forM fieldsTerms \fieldTerm0 -> liftIO $ do-                  -- Expand @(IO String, VarFieldValue)@ tuple term for each field-                  seqTerm hsc_env fieldTerm0 >>= \case-                    Term{subTerms=[ioStrTerm, varFieldValTerm]} -> do--                      fieldStr <- evalString interp (val ioStrTerm)--                      -- Expand VarFieldValue term-                      seqTerm hsc_env varFieldValTerm >>= \case-                        Term{subTerms=[unexpandedValueTerm]} -> do-                          actualValueTerm <- liftIO $ do-                            let val_ty = termType unexpandedValueTerm-                            cvObtainTerm hsc_env defaultDepth False{-don't force-} val_ty (val unexpandedValueTerm)-                          return (fieldStr, actualValueTerm)--                        _ -> error "impossible; expected VarFieldValue"-                    _ -> error "impossible; expected 2-tuple term"-              _ -> error "debugFields instance returned something other than VarFields"---- | Convert a Term representing a list @[a]@ to a list of the terms of type--- @a@, where @a@ is the given @'Type'@ arg.------ PRE-CON: Term represents a @[a]@-listTermToTermsList :: Term -> Debugger [Term]-listTermToTermsList Term{subTerms=[head_term, tail_term]}-  = do-    hsc_env <- getSession-    -- Expand next term:-    tail_term' <- liftIO $-      seqTerm hsc_env tail_term-    (head_term:) <$> listTermToTermsList tail_term'-listTermToTermsList _ = pure []-+            obtainParsedTerm "VarFields" 2 True varFieldsIOTy transformed_v varFieldsParser >>= \case+              Left _ -> pure Nothing+              Right res -> pure (Just res)+debugFieldsTerm term = do+  logSDoc Logger.Warning (text "debugValueTerm: Expecting" <+> ppr term <+> text "to be a Term or Suspension.")+  return Nothing
haskell-debugger/GHC/Debugger/Runtime/Instances/Discover.hs view
@@ -7,6 +7,9 @@   , RuntimeInstancesCache   , getDebugViewInstance   , emptyRuntimeInstancesCache++  -- * Finding runtime instances utils+  , compileAndLoadMthd   ) where  import Data.IORef@@ -105,7 +108,7 @@   case mhdv_uid of     Just hdv_uid -> do       let modl = mkModule (RealUnit (Definite hdv_uid)) debuggerViewClassModName-      let mthdRdrName mthStr = mkOrig modl (mkVarOcc mthStr)+      let mthdRdrName mthStr = mkOrig modl (mkVarOcc mthStr) :: RdrName        (err_msgs, res) <- liftIO $ runTcInteractive hsc_env $ do @@ -119,16 +122,16 @@          -- Try to compile and load an expression for all methods of `DebugView`         -- applied to the dictionary for the given Type (`needle_ty`)-        let debugValueMN  = mthdRdrName "debugValueIOWrapper"-            debugFieldsMN = mthdRdrName "debugFieldsIOWrapper"+        let debugValueME  = nlHsVar $ mthdRdrName "debugValueIOWrapper"+            debugFieldsME = nlHsVar $ mthdRdrName "debugFieldsIOWrapper"             debugValueWrapperMT =               mkVisFunTyMany needle_ty $                 mkTyConApp ioTyCon [mkListTy varValueIOTy]             debugFieldsWrapperMT =               mkVisFunTyMany needle_ty $                 mkTyConApp ioTyCon [mkListTy varFieldsIOTy]-        !debugValue_fval  <- compileAndLoadMthd debugValueMN  debugValueWrapperMT-        !debugFields_fval <- compileAndLoadMthd debugFieldsMN debugFieldsWrapperMT+        !debugValue_fval  <- compileAndLoadMthd debugValueME  debugValueWrapperMT+        !debugFields_fval <- compileAndLoadMthd debugFieldsME debugFieldsWrapperMT          let eval_opts = initEvalOpts (hsc_dflags hsc_env) EvalStepNone             interp    = hscInterp hsc_env@@ -170,18 +173,16 @@  -- | Try to compile and load a class method for the given type. ----- E.g. @compileAndLoadMthd "debugValue" (<ty> -> VarValue)@ returns the--- foreign value for an expression @debugValue@ applied to the dictionary for--- the requested type.-compileAndLoadMthd :: RdrName -- ^ Name of method/name of function that takes dictionary-                   -> Type    -- ^ The final type of expr when funct is alredy applied to dict+-- E.g. @compileAndLoadMthd (nlHsVar "foo") <ty>@ returns the+-- foreign value for an expression @foo@ applied to the dictionary required to+-- produce the final requested type+compileAndLoadMthd :: LHsExpr GhcPs -- ^ Expr of method/expr that takes dictionary+                   -> Type         -- ^ The final type of expr when funct is alredy applied to dict                    -> TcM ForeignHValue-compileAndLoadMthd mthName mthTy = do+compileAndLoadMthd expr mthTy = do   hsc_env <- getTopEnv -  let expr = nlHsVar mthName--  -- Rn, Tc, desugar applied to DebugView dictionary+  -- Rn, Tc, desugar applied to dictionary   (expr', _)    <- rnExpr (unLoc expr)   (expr'', wcs) <- captureConstraints $ tcExpr expr' (Check mthTy)   ev            <- simplifyTop wcs
haskell-debugger/GHC/Debugger/Runtime/Term/Cache.hs view
@@ -1,37 +1,12 @@ {-# LANGUAGE GADTs, DataKinds #-} module GHC.Debugger.Runtime.Term.Cache where -import GHC.Runtime.Eval import GHC.Types.Var.Env  import GHC.Debugger.Runtime.Term.Key  import Data.Map (Map) import qualified Data.Map as M------------------------------------------------------------------------------------- * Term Cache------------------------------------------------------------------------------------- | A term cache maps Names to Terms.------ We use the term cache to avoid redundant computation forcing (unique) names--- we've already forced before.------ A kind of trie map from 'TermKey's. The Map entry for no-path-fragments is--- the 'Term' of the original 'Id'.-type TermCache = TermKeyMap Term---- | Lookup a 'TermKey' in a 'TermCache'.--- Returns @Nothing@ for a cache miss and @Just@ otherwise.-lookupTermCache :: TermKey -> TermCache -> Maybe Term-lookupTermCache = lookupTermKeyMap---- | Inserts a 'Term' for the given 'TermKey' in the 'TermCache'.------ Overwrites existing values.-insertTermCache :: TermKey -> Term -> TermCache -> TermCache-insertTermCache = insertTermKeyMap  -------------------------------------------------------------------------------- -- * TermKeyMap
+ haskell-debugger/GHC/Debugger/Runtime/Term/Parser.hs view
@@ -0,0 +1,396 @@+{-# LANGUAGE LambdaCase, BlockArguments, OrPatterns #-}+-- | This module contains the 'TermParser' abstraction, which provides utilities for+-- interpreting and parsing 'Term's+module GHC.Debugger.Runtime.Term.Parser where++import Data.Functor+import Control.Applicative+import Control.Monad++import GHC+import GHC.Driver.Env+import GHC.Core.DataCon (dataConName)+import GHC.Plugins (falseDataCon, trueDataCon, splitFunTy, boolTy)+import GHC.Runtime.Eval+import GHC.Runtime.Heap.Inspect+import GHC.Runtime.Interpreter as Interp+import GHC.Types.Name (nameOccName)+import GHC.Types.Name.Occurrence (occNameString)+import qualified GHC.Debugger.Logger as Logger+import GHC.Utils.Outputable (text, (<+>), ppr)+import Control.Monad.Reader+import GHC.Core.TyCo.Compare+import GHC.Stack++import GHC.Debugger.Monad+import GHC.Debugger.Utils (expectRight)++import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote+import qualified GHC.Debugger.Runtime.Compile as Comp++-- | The main entry point for running the 'TermParser'.+obtainParsedTerm+  :: String+  -> Int+  -> Bool+  -> Type+  -> ForeignHValue+  -> TermParser a+  -> Debugger (Either [TermParseError] a)+obtainParsedTerm label depth force ty fhv term_parser = do+  hsc_env <- getSession+  term <- liftIO $ cvObtainTerm hsc_env depth force ty fhv+  runTermParserLogged label (checkType ty *> term_parser) term+++--------------------------------------------------------------------------------+-- * Term parser abstraction+--------------------------------------------------------------------------------++data TermParseError = TermParseError { getTermErrorMessage :: String }+  deriving (Eq, Show)++newtype TermParser a = TermParser { runTermParser :: Term -> Debugger (Either [TermParseError] a) }++liftDebugger :: Debugger a -> TermParser a+liftDebugger action = TermParser $ \_ -> Right <$> action++instance MonadIO TermParser where+  liftIO action = TermParser $ \_ -> Right <$> liftIO action++instance Functor TermParser where+  fmap f (TermParser p) = TermParser $ \term -> fmap (fmap f) (p term)++instance Applicative TermParser where+  pure x = TermParser $ \_ -> pure (Right x)+  TermParser pf <*> TermParser pa = TermParser $ \term -> do+    ef <- pf term+    case ef of+      Left err -> pure (Left err)+      Right f -> fmap (fmap f) (pa term)++instance Monad TermParser where+  TermParser pa >>= f = TermParser $ \term -> do+    ea <- pa term+    case ea of+      Left err -> pure (Left err)+      Right a -> runTermParser (f a) term++instance Alternative TermParser where+  empty = parseError (TermParseError "TermParser.empty")+  TermParser p1 <|> TermParser p2 = TermParser $ \term -> do+    res <- p1 term+    case res of+      Left e1 -> attachErrors e1 $ p2 term+      success -> pure success++instance MonadFail TermParser where+  fail s = parseError . TermParseError $ s++attachErrors :: [TermParseError] -> Debugger (Either [TermParseError] a)+                                 -> Debugger (Either [TermParseError] a)+attachErrors errs term_parser = do+  pres <- term_parser+  case pres of+    Left new_errs -> pure (Left $ errs ++ new_errs)+    Right res -> pure (Right res)++parseError :: TermParseError -> TermParser a+parseError err = TermParser $ \_ -> pure (Left [err])++termTag :: Term -> String+termTag Term{}         = "Term"+termTag Prim{}         = "Prim"+termTag Suspension{}   = "Suspension"+termTag NewtypeWrap{}  = "NewtypeWrap"+termTag RefWrap{}      = "RefWrap"++anyTerm :: TermParser Term+anyTerm = TermParser $ \term -> pure (Right term)++ensureTerm :: TermParser Term+ensureTerm = do+  t <- anyTerm+  case t of+    Term{} -> pure t+    other -> parseError (TermParseError $ "expected Term, got " <> termTag other)++checkType :: Type -> TermParser ()+checkType ty = do+  t <- anyTerm+  unless (termType t `eqType` ty) (parseError (TermParseError "ty mismatch"))++traceTerm :: TermParser ()+traceTerm = do+  t <- anyTerm+  liftDebugger $ logSDoc Logger.Debug (ppr t)++-- | Evaluate the currently focused term+seqTermP :: HasCallStack => TermParser a -> TermParser a+seqTermP term_parser = do+  t <- anyTerm+  hsc_env <- liftDebugger $ getSession+  focus (liftIO $ seqTerm hsc_env t)+        term_parser++-- | If a term is a suspension, make sure that it's a thunk and not just that we+-- reached the depth limit.+refreshTerm :: TermParser Term+refreshTerm = do+  t <- anyTerm+  case t of+    Suspension {} -> do+      t' <- foreignValueToTerm (ty t) (val t)+      return t'+    _ -> return t+++-- | Change the focus of the term parser onto the specified term.+focus :: TermParser Term -> TermParser a -> TermParser a+focus parse_term term_parser =+  parse_term >>= \t ->+    TermParser $ \_ -> runTermParser term_parser t++-- | Focus on a new subtree, after forcing it to WHNF.+focusSeq :: HasCallStack => TermParser Term -> TermParser a -> TermParser a+focusSeq parse_term term_parser = focus parse_term (seqTermP term_parser)++-- | Choose the nth subterm+subtermTerm :: Int -> TermParser Term+subtermTerm idx = do+  t <- anyTerm+  case t of+    Term{subTerms}+      | idx < length subTerms -> do+          liftDebugger $ logSDoc Logger.Debug (ppr subTerms)+          focus (pure (subTerms !! idx)) refreshTerm+      | otherwise -> parseError (TermParseError $ "missing subterm index " <> show idx)+    other -> parseError (TermParseError $ "expected Term with subterms, got " <> termTag other)++-- | Choose the nth subterm, force it to WHNF and run the supplied parser on it.+subtermWith :: Int -> TermParser a -> TermParser a+subtermWith idx term_parser = do+  focusSeq (subtermTerm idx) term_parser++matchOccNameTerm :: String -> a -> TermParser a+matchOccNameTerm occName result = do+  Term{dc} <- ensureTerm+  case dc of+    Left name | name == occName -> pure result+    _ -> empty++matchDataConTerm :: DataCon -> a -> TermParser a+matchDataConTerm dataCon result = do+  Term{dc} <- ensureTerm+  case dc of+    Right dc' | dc' == dataCon -> pure result+    _ -> empty++matchConstructorTerm :: String -> TermParser ()+matchConstructorTerm ctorName = do+  term <- anyTerm+  case term of+    t@Term{} | constructorName (dc t) == ctorName -> return ()+             | otherwise ->+                parseError (TermParseError ("expected: "+                                            ++ ctorName+                                            ++ " got: "+                                            ++ constructorName (dc t)))+    other ->+      parseError (TermParseError ("expected Program term, got " <> termTag other))++constructorName :: Either String DataCon -> String+constructorName = \case+  Left name -> name+  Right dataCon -> occNameString . nameOccName $ dataConName dataCon++newtypeWrapParser :: TermParser Term+newtypeWrapParser = do+  t <- anyTerm+  case t of+    NewtypeWrap{wrapped_term} -> pure wrapped_term+    other -> parseError (TermParseError $ "expected NewtypeWrap, got " <> termTag other)++-- | Parse a primitive value as a single word (Prim term)+primParser :: TermParser Word+primParser = do+  t <- anyTerm+  case t of+    Prim{valRaw=[w64_tid]} -> pure w64_tid+    other -> do+      parseError (TermParseError $ "expected a Prim term, got " <> termTag other)++-- | Is the current focus a suspension?+isSuspension :: TermParser Bool+isSuspension = focus refreshTerm $ do+  t <- anyTerm+  traceTerm+  case t of+    Suspension{} -> pure True+    other -> do+      liftDebugger $ logSDoc Logger.Debug (text $ termTag other)+      return False++-- | Obtain a Term from a ForeignHValue+foreignValueToTerm :: Type -> ForeignHValue -> TermParser Term+foreignValueToTerm ty fhv =+  liftDebugger $ do+    hsc_env <- getSession+    liftIO $ cvObtainTerm hsc_env 2 False ty fhv++--------------------------------------------------------------------------------+-- * Logging parsers+--------------------------------------------------------------------------------++logTermParserMsg :: String -> String -> Debugger ()+logTermParserMsg label msg =+  logSDoc Logger.Debug (text "[TermParser]" <+> text label <+> text msg)++runTermParserLogged+  :: String+  -> TermParser a+  -> Term+  -> Debugger (Either [TermParseError] a)+runTermParserLogged label term_parser term = do+  logTermParserMsg label "start"+  res <- runTermParser term_parser term+  case res of+    Left errs -> do+      logTermParserMsg label ("failed: " ++ unlines (map getTermErrorMessage errs))+      pure (Left errs)+    Right a -> do+      logTermParserMsg label "succeeded"+      pure (Right a)++--------------------------------------------------------------------------------+-- * Base parsers+--------------------------------------------------------------------------------++tuple2Of :: TermParser a -> TermParser b -> TermParser (a, b)+tuple2Of parserA parserB = (,) <$> subtermWith 0 parserA <*> subtermWith 1 parserB++boolParser :: TermParser Bool+boolParser =+  matchOccNameTerm "False" False+    <|> matchOccNameTerm "True" True+    <|> matchDataConTerm falseDataCon False+    <|> matchDataConTerm trueDataCon True+    <|> parseError (TermParseError "expected Bool term")++-- | Parse a list, given a parser for each element.+-- The whole list will be forced.+parseList :: TermParser a -> TermParser [a]+parseList item_parser =+        (matchConstructorTerm "[]" *> pure [])+    <|> (matchConstructorTerm ":" *> ((:) <$> subtermWith 0 item_parser <*> subtermWith 1 (parseList item_parser)))++-- | Parse an 'Int'+intParser :: TermParser Int+intParser = fromIntegral <$> wordParser++-- | Parse a 'Word'+wordParser :: TermParser Word+wordParser = subtermWith 0 primParser++-- | Parse a 'String' term+stringParser :: TermParser String+stringParser = do+  Term{val=string_fv} <- anyTerm+  liftDebugger $+    expectRight =<< Remote.evalString (Remote.untypedRef string_fv)++-- | Parse a 'Maybe' something+maybeParser :: TermParser a -> TermParser (Maybe a)+maybeParser just_p = do+  (matchConstructorTerm "Nothing" $> Nothing)+  <|> (matchConstructorTerm "Just" *> (Just <$> subtermWith 0 just_p))++--------------------------------------------------------------------------------+-- * VarValue+--------------------------------------------------------------------------------++-- | Parse a term which is a 'ValValue'+varValueParser :: TermParser (Term, Bool)+varValueParser =+  (,) <$> subtermWith 0 programTermParser <*> subtermWith 1 boolParser++--------------------------------------------------------------------------------+-- * VarFields+--------------------------------------------------------------------------------++-- | Parse a term which is a 'Program VarFields'+varFieldsParser :: TermParser [(String, Term)]+varFieldsParser =+  focusSeq newtypeWrapParser $+    -- Program [(IO String, VarFieldValue)]+    focusSeq programTermParser $+        -- [(IO String, VarFieldValue)]+        parseList parseFieldItem++  where+    -- Parses an item of type (IO String, VarFieldValue)+    parseFieldItem :: TermParser (String, Term)+    parseFieldItem = (,) <$> subtermWith 0 parseFieldLabel <*> subtermWith 1 varFieldValueParser++    parseFieldLabel :: TermParser String+    parseFieldLabel = do+      ioStrTerm <- anyTerm+      interp <- liftDebugger $ hscInterp <$> getSession+      case ioStrTerm of+        (Suspension{} ; Term{})+          -> liftIO $ evalString interp (val ioStrTerm)+        _ -> parseError (TermParseError "parseFieldLabel expected a val")++varFieldTupleParser :: TermParser (Term, Term)+varFieldTupleParser = tuple2Of anyTerm anyTerm++varFieldValueParser :: TermParser Term+varFieldValueParser = subtermTerm 0++--------------------------------------------------------------------------------+-- * Program Parser+--------------------------------------------------------------------------------++-- | Parses and evaluates a "Program" term.+programTermParser :: TermParser Term+programTermParser =+        programPureParser+    <|> programApParser+    <|> programBranchParser+    <|> programAskThunkParser+  where+    programPureParser = do+      matchConstructorTerm "PureProgram"+      subtermTerm 0++    programApParser = do+      matchConstructorTerm "ProgramAp"+      p1 <- subtermWith 0 programTermParser+      p2 <- subtermWith 1 programTermParser+      case (p1, p2) of+        ( (Suspension{} ; Term{}), (Suspension{} ; Term{}) ) -> do+          let (_, _arg_ty, res_ty) = splitFunTy (termType p1)+          res <- liftDebugger $+            expectRight =<< Remote.eval+              (Remote.untypedRef (val p1) `Remote.app` Remote.untypedRef (val p2))+          foreignValueToTerm res_ty res+        _ -> parseError (TermParseError "programApParser: expected two vals")++    programBranchParser = do+      matchConstructorTerm "ProgramBranch"+      cond <- subtermWith 0 (focusSeq programTermParser boolParser)+      if cond then do+            subtermWith 1 programTermParser+           else do+            subtermWith 2 programTermParser++    programAskThunkParser = do+      matchConstructorTerm "ProgramAskThunk"+      -- Get what we need to check THUNKiness for+      is_thunk <- focus (subtermTerm 1) isSuspension+      bool_fv <- liftDebugger $ reifyBool is_thunk+      foreignValueToTerm boolTy bool_fv++reifyBool :: Bool -> Debugger ForeignHValue+reifyBool b = Comp.compileRaw (show b ++ ":: Bool")+
+ haskell-debugger/GHC/Debugger/Runtime/Thread.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE QualifiedDo #-}+{-# LANGUAGE OrPatterns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ViewPatterns #-}+-- |+-- TODO+-- - [] Consider caching once and forall the expressions we dynamically compile and load in this module.+module GHC.Debugger.Runtime.Thread+  ( getRemoteThreadIdFromRemoteContext+  , getRemoteThreadId+  , getRemoteThreadsLabels+  , listAllLiveRemoteThreads+  ) where++import Data.Maybe+import Data.Functor+import Control.Applicative+import Control.Concurrent+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Reader+import Data.IORef+import GHC.Conc.Sync++import GHC.Builtin.Types+import GHC.Runtime.Heap.Inspect+import GHC.Utils.Outputable++import GHCi.Message+import GHCi.RemoteTypes++import GHC.Debugger.Utils+import GHC.Debugger.Logger as Logger+import GHC.Debugger.Monad+import GHC.Debugger.Interface.Messages+import GHC.Debugger.Runtime.Term.Parser+import GHC.Debugger.Runtime.Thread.Map++import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote+import qualified GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin as Remote++-- | Get a 'RemoteThreadId' from a remote 'ResumeContext' gotten from an 'ExecBreak'+getRemoteThreadIdFromRemoteContext :: ForeignRef (ResumeContext [HValueRef]) -> Debugger RemoteThreadId+getRemoteThreadIdFromRemoteContext fctxt = do+  -- Get the ResumeContext term and fetch the resumeContextThreadId field+  parsed_threadid <- obtainParsedTerm "RemoteContext's ThreadId" 2 True anyTy (castForeignRef fctxt)+                        (subtermWith 2{-RemoteContext's ThreadId-} anyTerm)+  case parsed_threadid of+    Left errs -> do+      logSDoc Logger.Error (vcat (map (text . getTermErrorMessage) errs))+      liftIO $ fail "Failed to parse remote ResumeContext's thread id"+    Right Term{val=threadIdVal} -> do+      getRemoteThreadId (castForeignRef threadIdVal)+    _ -> liftIO $ fail "Expected threadIdTerm to be a Term!"++getRemoteThreadId :: ForeignRef ThreadId -> Debugger RemoteThreadId+getRemoteThreadId threadIdRef = do+  thread_id_fv <- expectRight =<< Remote.eval+    (Remote.fromThreadId (Remote.ref threadIdRef))++  parsed_int <-+    obtainParsedTerm "ThreadId's Int value" 2 True wordTy{-really, Word64, but we won't look at the type-} (castForeignRef thread_id_fv) intParser++  case parsed_int of+    Left errs -> do+      logSDoc Logger.Error (vcat (map (text . getTermErrorMessage) errs))+      liftIO $ fail "Failed to parse remote thread id on fromThreadId result!"+    Right tid_int -> do++      tmap_ref <- asks threadMap+      -- unconditionally write to the map the foreign ref (it should always+      -- refer to the same ThreadId as a possible existing entry)+      liftIO $ modifyIORef' tmap_ref $+        insertThreadMap tid_int threadIdRef++      return (RemoteThreadId tid_int)++-- | Is the remote thread running or blocked (NOT finished NOR dead)?+getRemoteThreadStatus :: ForeignRef ThreadId -> Debugger ThreadStatus+getRemoteThreadStatus threadIdRef = do+  status_fv  <- expectRight =<< Remote.evalIO+    (Remote.threadStatus (Remote.ref threadIdRef))+  status_parsed <-+    obtainParsedTerm "ThreadStatus" 2 True anyTy{-..no..-} (castForeignRef status_fv) threadStatusParser++  case status_parsed of+    Left errs -> do+      logSDoc Logger.Error (vcat (map (text . getTermErrorMessage) errs))+      liftIO $ fail "Failed to parse ThreadStatus"+    Right thrdStatus ->+      return thrdStatus++isRemoteThreadLive :: ForeignRef ThreadId -> Debugger Bool+isRemoteThreadLive r = getRemoteThreadStatus r <&> \case+  (ThreadRunning ; ThreadBlocked{}) -> True+  (ThreadDied    ; ThreadFinished)  -> False++-- | Call 'listThreads' on the (possibly) remote debuggee process to get the+-- list of threads running on the debuggee. Filter by running threads+-- This may include the debugger threads if using the internal interpreter.+listAllLiveRemoteThreads :: Debugger [(RemoteThreadId, ForeignRef ThreadId)]+listAllLiveRemoteThreads = do+  threads_fvs <- expectRight =<< Remote.evalIOList Remote.listThreads+  catMaybes <$> do+    forM threads_fvs $ \(castForeignRef -> thread_fv) -> do+      isLive <- isRemoteThreadLive thread_fv+      if isLive then do+        tid <- getRemoteThreadId thread_fv+        pure $ Just (tid, thread_fv)+      else do+        pure Nothing++-- | Get the label of a Thread in a remote process. Returns one element per+-- given Thread with @Just string@ if a label was found.+getRemoteThreadsLabels :: [ForeignRef ThreadId] -> Debugger [Maybe String]+getRemoteThreadsLabels threadIdRefs = do++  forM threadIdRefs $ \threadIdRef -> do+    +    r <- Remote.evalIOList $ Remote.do+      mb_str <- Remote.threadLabel (Remote.ref threadIdRef)+      Remote.return (Remote.maybeToList mb_str)++    expectRight r >>= \case+      []          -> pure Nothing+      [io_lbl_fv] -> Just <$> (expectRight =<< Remote.evalString (Remote.ref io_lbl_fv))+      _ -> liftIO $ fail "Unexpected result from evaluating \"threadLabel\""++--------------------------------------------------------------------------------+-- * TermParsers+--------------------------------------------------------------------------------++-- ** Threads ------------------------------------------------------------------++threadStatusParser :: TermParser ThreadStatus+threadStatusParser = do+        (matchConstructorTerm "ThreadRunning"  $> ThreadRunning)+    <|> (matchConstructorTerm "ThreadFinished" $> ThreadFinished)+    <|> (matchConstructorTerm "ThreadDied"     $> ThreadDied)+    <|> (matchConstructorTerm "ThreadBlocked"  *> (ThreadBlocked <$> subtermWith 0 blockedReasonParser))++blockedReasonParser :: TermParser BlockReason+blockedReasonParser = do+        (matchConstructorTerm "BlockedOnMVar"        $> BlockedOnMVar)+    <|> (matchConstructorTerm "BlockedOnBlackHole"   $> BlockedOnBlackHole)+    <|> (matchConstructorTerm "BlockedOnException"   $> BlockedOnException)+    <|> (matchConstructorTerm "BlockedOnSTM"         $> BlockedOnSTM)+    <|> (matchConstructorTerm "BlockedOnForeignCall" $> BlockedOnForeignCall)+    <|> (matchConstructorTerm "BlockedOnOther"       $> BlockedOnOther)+
+ haskell-debugger/GHC/Debugger/Runtime/Thread/Map.hs view
@@ -0,0 +1,42 @@+-- | A map to track and manage the debuggee runtime threads+module GHC.Debugger.Runtime.Thread.Map+  ( ThreadMap+  , emptyThreadMap++  -- * Operations+  , insertThreadMap+  , lookupThreadMap++  , threadMapToList++  -- can we detect when a thread has died? what happens if we have a reference+  -- to a ThreadId which has been GC'd?+  ) where++import Control.Concurrent+import Data.Coerce++import GHCi.RemoteTypes+import qualified Data.IntMap as IM++-- | A thread map maintains a mapping between the int thread identifier, which+-- uniquely identifies a thread spawned by the debuggee, and the (possibly+-- remote) reference to the thread (i.e. the corresponding ThreadId)+type ThreadMap = IM.IntMap (ForeignRef ThreadId)++-- | Insert a remote 'ThreadId' at this unique Int thread identifier+insertThreadMap :: Int -> ForeignRef ThreadId -> ThreadMap -> ThreadMap+insertThreadMap = IM.insert++-- | Lookup a remote 'ThreadId' by its unique Int identifier+lookupThreadMap :: Int -> ThreadMap -> Maybe (ForeignRef ThreadId)+lookupThreadMap = IM.lookup++-- | > It's empty, what did you expect?+emptyThreadMap :: ThreadMap+emptyThreadMap = IM.empty++-- | Get all the remote thread references from the ThreadMap+threadMapToList :: ThreadMap -> [(Int, ForeignRef ThreadId)]+threadMapToList = coerce . IM.toList+
+ haskell-debugger/GHC/Debugger/Runtime/Thread/Stack.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE OrPatterns #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE QualifiedDo #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ViewPatterns #-}++-- | Decoding the stack of a thread at runtime+module GHC.Debugger.Runtime.Thread.Stack+  ( getRemoteThreadStackCopy+  , getRemoteThreadIPEStack+  ) where++import Data.Bits+import Data.Maybe+import Control.Applicative+import Control.Concurrent+import Control.Monad+import Control.Monad.IO.Class+import GHC.Stack.CloneStack+import GHC.Exts.Heap.ClosureTypes+import GHC.Utils.Encoding.UTF8++import GHC+import GHC.Builtin.Types+import GHC.Runtime.Heap.Inspect++import GHC.Driver.Env+import GHC.Runtime.Interpreter as Interp+import GHC.Utils.Outputable++import GHCi.Message+import GHCi.RemoteTypes++import GHC.Debugger.Utils+import GHC.Debugger.Logger as Logger+import GHC.Debugger.Monad+import GHC.Debugger.Runtime.Term.Parser+import GHC.Debugger.Runtime.Eval+import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote+import qualified GHC.Debugger.Runtime.Eval.RemoteExpr.Builtin as Remote++-- | Clone the stack of the given remote thread and get the breakpoint ids of available frames+getRemoteThreadStackCopy :: ForeignRef ThreadId -> Debugger [InternalBreakpointId]+getRemoteThreadStackCopy threadIdRef = do++  l <- Remote.evalIOList $ Remote.do+    clonedStack <- Remote.cloneThreadStack (Remote.ref threadIdRef)+    frames      <- Remote.decodeStack      clonedStack+    Remote.return (Remote.ssc_stack frames)++  case l of+    Left (EvalRaisedException e) -> do+      logSDoc Logger.Info (text "Failed to decode the stack with" <+> text (show e) $$ text "This is likely bug #26640 in the decoder, which has been fixed for 9.14.2 and forward. No StackTrace will be returned...")+      return []+    Left e -> do+      logSDoc Logger.Warning (text "Failed to decode the stack with" <+> text (show e) $$ text "No StackTrace will be returned...")+      return []+    Right stack_frames_fvs -> fmap (catMaybes . catMaybes) $+      forM stack_frames_fvs $ \stack_frame_fv ->+        obtainParsedTerm "ghc-heap:StackFrame" 2 True anyTy{-todo:stackframety?-} (castForeignRef stack_frame_fv)+          ((Just <$> retBCOParser) <|> pure Nothing) >>= \case+            Left errs -> do+              logSDoc Logger.Error (vcat (map (text . getTermErrorMessage) errs))+              return Nothing+            Right tm ->+              return tm++-- | Try to get an IPE stacktrace.+--+-- At the moment, we assume IPE stacktraces are always empty @[]@ for threads+-- with interpreter frames.+--+-- Really, as long as there is IPE information, this function should return+-- StackEntries for all frames, including the interpreter ones, since these are+-- typically be interleaved with "normal" frames.+--+getRemoteThreadIPEStack :: ForeignRef ThreadId -> Debugger [StackEntry]+getRemoteThreadIPEStack threadIdRef = do+  l <- Remote.evalIOList $ Remote.do+    clonedStack <- Remote.cloneThreadStack (Remote.ref threadIdRef)+    Remote.decode clonedStack+  case l of+    Left (EvalRaisedException e) -> do+      logSDoc Logger.Info (text "Failed to decode the stack with" <+> text (show e) $$ text "This is likely bug #26640 in the decoder, which has been fixed for 9.14.2 and forward. No StackTrace could be produced...")+      return []+    Left e -> do+      logSDoc Logger.Warning (text "Failed to decode the stack with" <+> text (show e) $$ text "No StackTrace will be returned...")+      return []+    Right entries_fvs -> do+      mapM (\entry_fv -> do+          stack_entry <- obtainParsedTerm "StackEntry" 2 True anyTy{-stackentry, but we won't look at the ty...-} entry_fv stackEntryParser+          case stack_entry of+            Left errs -> do+              logSDoc Logger.Error (vcat (map (text . getTermErrorMessage) errs))+              liftIO $ fail "Failed to parse @StackEntry@ from decoding thread stack!"+            Right se ->+              pure se+        ) (map castForeignRef entries_fvs)++--------------------------------------------------------------------------------+-- ** Decoding Stack Frames ----------------------------------------------------+--------------------------------------------------------------------------------++stackEntryParser :: TermParser StackEntry+stackEntryParser = do+    StackEntry <$> subtermWith 0 stringParser <*> subtermWith 1 stringParser <*> subtermWith 2 stringParser <*> pure INVALID_OBJECT{-this is a stub-}++retBCOParser :: TermParser (Maybe InternalBreakpointId)+retBCOParser = do+  -- Match against "RetBCO" frames and extract the BCOClosure information+  Suspension{val, ctype=BCO}+    {-"the otherwise case: Unknown closure", hence Suspension-}+      <- matchConstructorTerm "RetBCO" *> subtermWith 1 (subtermWith 0{-take from Box-} anyTerm)+  liftDebugger $ do++    -- Decode the BCO closure using 'getClosureData' on the foreign heap+    bco_closure_fv <- expectRight =<< Remote.evalIO+      (Remote.getClosureData (Remote.ref (castForeignRef val)))++    r <- obtainParsedTerm "BCO BRK_FUN info" 2 True anyTy (castForeignRef bco_closure_fv) bcoInternalBreakpointId+    case r of+      Left err -> liftIO $ fail (show err)+      Right t  -> return t++-- | Parse an 'InternalBreakpointId' out of a 'BCOClosure' term.+bcoInternalBreakpointId :: TermParser (Maybe InternalBreakpointId)+bcoInternalBreakpointId = do+  mbcpIxs <- bcoBreakPointInfoParser+  case mbcpIxs of+    Nothing -> return Nothing+    Just BCOBreakPointInfo{..} -> do+      mod_name <- bcoLiteralString info_mod_name_ix+      mod_id   <- bcoLiteralString info_mod_id_ix++      return $ Just $ evalBreakpointToId EvalBreakpoint+        { eb_info_mod      = mod_name+        , eb_info_mod_unit = utf8EncodeShortByteString mod_id+        , eb_info_index    = fromIntegral $ brk_info_ix_hi .<<. 16 + brk_info_ix_lo+        }++-- | Parse a literal 'String' from a BCO given a valid index into the literals array+bcoLiteralString :: Word -> TermParser String+bcoLiteralString ix = do+  Term{val=literals_fv} <- subtermWith 2 (subtermTerm 0{-Box's field-})+  liftDebugger $ do++    r <- Remote.evalIOString $+        Remote.peekCString $+          Remote.withUnboxed (Remote.lit (fromIntegral ix))+            (Remote.indexAddrArray (Remote.untypedRef literals_fv))++    expectRight r++-- | The indexes found in the BRK_FUN instruction+data BCOBreakPointInfo = BCOBreakPointInfo+  { brk_array_ix     :: !Word+  , info_mod_name_ix :: !Word+  , info_mod_id_ix   :: !Word+  , brk_info_ix_hi   :: !Word+  , brk_info_ix_lo   :: !Word+  }+  deriving Show++-- | Parses a 'BCOBreakPoint' if the current term is a 'BCOClosure' headed by a+-- BRK_FUN bytecode instruction.+-- Returns Nothing if the 'BCOClosure' instructions are headed by a BRK_FUN.+bcoBreakPointInfoParser :: TermParser (Maybe BCOBreakPointInfo)+bcoBreakPointInfoParser = do+  Term{val=instrs_array_fv} <- subtermWith 1{-instrs field-} (subtermTerm 0{-Box's field-})+  -- highly internals dependent...+  -- find the BCI at index 0. bci is word16. the first 8bits are for flags+  -- something something BCO_READ_LARGE_ARG with (index_at 0#) rather than always BCO_NEXT?+  liftDebugger $ do+    hsc_env <- getSession++    -- The BRK_FUN is the first instruction, unless BCO_NAME is enabled, in+    -- which case it's the second.+    let bRK_FUN_offset+          | gopt Opt_AddBcoName (hsc_dflags hsc_env) = 2 -- BCO_NAME + ptrs ix.+          | otherwise = 0 :: Int++    let find_ixs_fv = Remote.raw $+          "\\x -> let index_at n = GHC.Word.W16# (GHC.Base.indexWord16Array# x (n GHC.Exts.+# " ++ show bRK_FUN_offset ++ "#)) " +++                   "in if (index_at 0# Data.Bits..&. 0xFF) == 66{-bci_BRK_FUN-} then \+                        Just (index_at 1#, index_at 2#, index_at 3#, index_at 4#, index_at 5#) \+                      else Nothing"+    rs_fv <- expectRight =<< Remote.eval+      (find_ixs_fv `Remote.app` Remote.untypedRef instrs_array_fv)++    mparsed_bco_brk <- obtainParsedTerm "Ixs" maxBound True anyTy rs_fv $+      maybeParser $ BCOBreakPointInfo <$>+        subtermWith 0 wordParser <*> subtermWith 1 wordParser <*> subtermWith 2 wordParser+                                 <*> subtermWith 3 wordParser <*> subtermWith 4 wordParser+    case mparsed_bco_brk of+      Left errs -> do+        logSDoc Logger.Error (vcat (map (text . getTermErrorMessage) errs))+        liftIO $ fail "Failed to parse BCOClosure's BRK_FUN"+      Right r -> return r
haskell-debugger/GHC/Debugger/Session.hs view
@@ -163,8 +163,7 @@   initial_home_graph <- createUnitEnvFromFlags dflags0 unitDflags   let home_units = unitEnv_keys initial_home_graph   init_home_unit_graph <- forM initial_home_graph $ \homeUnitEnv -> do-    let cached_unit_dbs = homeUnitEnv_unit_dbs homeUnitEnv-        dflags = homeUnitEnv_dflags homeUnitEnv+    let dflags = homeUnitEnv_dflags homeUnitEnv         old_hpt = homeUnitEnv_hpt homeUnitEnv     (dbs,unit_state,home_unit,mconstants) <- State.initUnits (hsc_logger env) dflags Nothing home_units     updated_dflags <- GHC.updatePlatformConstants dflags mconstants
haskell-debugger/GHC/Debugger/Stopped.hs view
@@ -3,7 +3,10 @@    TypeApplications, ScopedTypeVariables, BangPatterns #-} module GHC.Debugger.Stopped where +import Control.Monad import Control.Monad.Reader+import Data.IORef+import GHC.Stack.CloneStack as Stack  import GHC import GHC.Types.Unique.FM@@ -13,18 +16,23 @@ import GHC.Unit.Home.ModInfo import GHC.Unit.Module.ModDetails import GHC.Types.TypeEnv-import GHC.Data.Maybe (expectJust)+import GHC.Data.Maybe import GHC.Driver.Env as GHC import GHC.Runtime.Debugger.Breakpoints as GHC import GHC.Runtime.Eval import GHC.Types.SrcLoc+import GHC.Utils.Outputable as Ppr import qualified GHC.Unit.Home.Graph as HUG  import GHC.Debugger.Stopped.Variables import GHC.Debugger.Runtime+import GHC.Debugger.Runtime.Thread+import GHC.Debugger.Runtime.Thread.Stack+import GHC.Debugger.Runtime.Thread.Map import GHC.Debugger.Monad import GHC.Debugger.Interface.Messages import GHC.Debugger.Utils+import qualified GHC.Debugger.Logger as Logger  {- Note [Don't crash if not stopped]@@ -47,29 +55,139 @@ -}  ----------------------------------------------------------------------------------- * Stack trace+-- * Threads -------------------------------------------------------------------------------- --- | Get the stack frames at the point we're stopped at-getStacktrace :: Debugger [StackFrame]-getStacktrace = GHC.getResumeContext >>= \case-  [] ->-    -- See Note [Don't crash if not stopped]-    return []-  r:_-    | Just ss <- srcSpanToRealSrcSpan (GHC.resumeSpan r)-    -> return-        [ StackFrame-          { name = GHC.resumeDecl r-          , sourceSpan = realSrcSpanToSourceSpan ss+getThreads :: Debugger [DebuggeeThread]+getThreads = do+  -- TODO: we want something more like 'listThreads', but ensure that we only+  -- report the threads of the debuggee (and not the debugger, if they+  -- are the same process). Perhaps the solution is to not allow them to be in+  -- the same process, in which case 'listThreads' would be correct as is by+  -- construction.+  --+  -- For now, we approximate by just listing out the ThreadsMap, under the+  -- assumption the debugger client will only care about threads we've already+  -- stopped at (which are the only ones we've inserted in the threads map),+  -- but for full multi threaded debugging we need the listThreads.+  --+  -- tmap <- liftIO . readIORef =<< asks threadMap+  -- let (t_ids, remote_refs) = unzip (threadMapToList tmap)+  --+  -- Oh, try the listThreads just for fun.+  (t_ids, remote_refs) <- unzip <$> listAllLiveRemoteThreads+  t_labels             <- getRemoteThreadsLabels remote_refs+  let+    _mkDebuggeeThread tid tlbl+      = DebuggeeThread+        { tId = tid+        , tName = tlbl+        }+    _all_threads+      = zipWith _mkDebuggeeThread t_ids t_labels++  -- TODO: We ignore _all_threads and report only the main execution thread for now.+  GHC.getResumeContext >>= \case+    [] ->+      -- See Note [Don't crash if not stopped]+      return []+    r:_ -> do+      r_tid <- getRemoteThreadIdFromRemoteContext (GHC.resumeContext r)+      return+        [ DebuggeeThread+          { tId = r_tid+          , tName = Just "Main Thread"           }         ]-    | otherwise ->-        -- No resume span; which should mean we're stopped on an exception.-        -- No info for now.-        return []  --------------------------------------------------------------------------------+-- * Stack trace+--------------------------------------------------------------------------------++-- | Get the stack frames at the point we're stopped at+getStacktrace :: RemoteThreadId -> Debugger [DbgStackFrame]+getStacktrace req_tid = do++  tm <- liftIO . readIORef =<< asks threadMap+  let m_f_tid = lookupThreadMap (remoteThreadIntRef req_tid) tm+  ipe_stack <- case m_f_tid of+    Nothing -> pure []+    Just f_tid -> do+      -- For now, assume that if we can get an IPE backtrace then this thread+      -- is compiled and doesn't have any interpreter frames.+      -- Of course, this isn't true since we should also be able to get an IPE+      -- backtrace for interpreter threads. Just not yet.+      --+      -- If IPE is empty => it's not an interpreter thread => the IPE backtrace+      --  is the best we can do, so just return that.+      getRemoteThreadIPEStack f_tid++  case ipe_stack of+    [] -> do+      _decoded_frames <- case m_f_tid of+        Nothing -> pure []+        Just f_tid -> do+          hsc_env <- getSession+          ibis <- getRemoteThreadStackCopy f_tid+          let hug = hsc_HUG hsc_env+          forM ibis $ \ibi -> do+            info_brks <- liftIO $ readIModBreaks hug ibi+            let modl  = getBreakSourceMod ibi info_brks+            srcSpan   <- liftIO $ getBreakLoc (readIModModBreaks hug) ibi info_brks++            -- Find function name+            -- TODO: Cache moduleLineMap?+            ticks <- fromMaybe (error "getStacktrace:getTicks") <$> makeModuleLineMap modl+            let current_toplevel_decl = enclosingTickSpan ticks srcSpan++            modl_str  <- display modl+            return DbgStackFrame+              { name = modl_str ++ "." -- ++ Stack.functionName stack_entry+              , sourceSpan = realSrcSpanToSourceSpan $ current_toplevel_decl -- realSrcSpan srcSpan+              }++      -- Try decoding a stack with interpreter continuation frames (RetBCOs) and use the BRK_FUN src locations.+      -- Add the latest resume context at the head.+      head_frame <- GHC.getResumeContext >>= \case+        [] ->+          -- See Note [Don't crash if not stopped]+          return Nothing+        r:_+          | Just ss <- srcSpanToRealSrcSpan (GHC.resumeSpan r)+          -> do+            r_tid <- getRemoteThreadIdFromRemoteContext (GHC.resumeContext r)+            if r_tid == req_tid then+              -- We're getting the stacktrace for the thread we're stopped at.+              return $+                Just DbgStackFrame+                  { name = GHC.resumeDecl r+                  , sourceSpan = realSrcSpanToSourceSpan ss+                  }+            else+             return Nothing+          | otherwise ->+              -- No resume span; which should mean we're stopped on an exception.+              -- No info for now.+              return Nothing+      -- TODO: Currently, only display head_frame+      -- return (maybe id (:) head_frame $ decoded_frames)+      return (maybe [] (:[]) head_frame)+    ipe_frames -> catMaybes <$> do+      forM ipe_frames $ \stack_entry -> do+        case srcSpanStringToSourceSpan (Stack.srcLoc stack_entry) of+          Left err -> do+            -- Couldn't parse. The srcLoc may be invalid so just keep this as info, not warning.+            logSDoc Logger.Info $+              text "Couldn't parse StackEntry srcLoc \"" Ppr.<> text (Stack.srcLoc stack_entry)+                                                         Ppr.<> text "\":" <+> text err+            return Nothing+          Right sourceSpan ->+            return $ Just DbgStackFrame+              { name = Stack.moduleName stack_entry ++ "." ++ Stack.functionName stack_entry+              , sourceSpan = sourceSpan+              }++-------------------------------------------------------------------------------- -- * Scopes -------------------------------------------------------------------------------- @@ -167,7 +285,7 @@                 -- It is a "lazy" DAP variable: our reply can ONLY include                 -- this single variable. -                term' <- forceTerm key term+                term' <- forceTerm term                  vi <- termToVarInfo key term' @@ -185,7 +303,7 @@        -- (VARR)(a) from here onwards -      LocalVariables -> fmap Right $+      LocalVariables -> fmap Right $ do         -- bindLocalsAtBreakpoint hsc_env (GHC.resumeApStack r) (GHC.resumeSpan r) (GHC.resumeBreakpointId r)         mapM tyThingToVarInfo =<< GHC.getBindings 
haskell-debugger/GHC/Debugger/Stopped/Variables.hs view
@@ -3,7 +3,6 @@    TypeApplications, ScopedTypeVariables, BangPatterns, DerivingVia, TypeAbstractions #-} module GHC.Debugger.Stopped.Variables where -import Data.IORef import Control.Monad.Reader  import GHC@@ -16,14 +15,11 @@ import qualified GHC.Runtime.Debugger     as GHCD import qualified GHC.Runtime.Heap.Inspect as GHCI -import GHC.Debugger.View.Class hiding (VarFields)- import GHC.Debugger.Monad import GHC.Debugger.Interface.Messages import GHC.Debugger.Runtime import GHC.Debugger.Runtime.Instances import GHC.Debugger.Runtime.Term.Key-import GHC.Debugger.Runtime.Term.Cache import GHC.Debugger.Utils  -- | 'TyThing' to 'VarInfo'. The 'Bool' argument indicates whether to force the@@ -164,7 +160,7 @@             , isThunk = False             , varValue, varRef } -        Just VarValue{varExpandable, varValue=value} -> do+        Just VarValueResult{varValueResultExpandable=varExpandable, varValueResult=value} -> do            varRef <-             if varExpandable@@ -191,13 +187,8 @@ -- | Forces a term to WHNF -- -- The term is updated in the cache at the given key.-forceTerm :: TermKey -> Term -> Debugger Term-forceTerm key term = do+forceTerm :: Term -> Debugger Term+forceTerm term = do   hsc_env <- getSession--  term' <- liftIO $ seqTerm hsc_env term--  -- update cache with the forced term right away instead of invalidating it.-  asks termCache >>= \r -> liftIO $ modifyIORef' r (insertTermCache key term')-  return term'+  liftIO $ seqTerm hsc_env term 
haskell-debugger/GHC/Debugger/Utils.hs view
@@ -7,14 +7,22 @@   , module GHC.Utils.Trace   ) where +import Control.Applicative+import Control.Monad.IO.Class+import Control.Exception+ import GHC import GHC.Data.FastString import GHC.Driver.DynFlags import GHC.Driver.Ppr-import GHC.Utils.Outputable+import GHC.Utils.Outputable hiding (char) import GHC.Utils.Trace+import qualified Data.Text as T +import Data.Attoparsec.Text+ import GHC.Debugger.Monad+import GHC.Debugger.Logger as Logger import GHC.Debugger.Interface.Messages  --------------------------------------------------------------------------------@@ -37,4 +45,52 @@   dflags <- getDynFlags   return $ showSDoc dflags (ppr x) {-# INLINE display #-}++--------------------------------------------------------------------------------+-- * More utils+--------------------------------------------------------------------------------++expectRight :: Exception e => Either e a -> Debugger a+expectRight s = case s of+  Left e -> do+    logSDoc Logger.Error (text $ displayException e)+    liftIO $ throwIO e+  Right a -> do+    pure a++--------------------------------------------------------------------------------+-- * Parsing+--------------------------------------------------------------------------------++-- | Takes a 'srcLoc' string from 'StackEntry' and returns a 'SourceSpan'.+--+-- === Example strings+--+-- - @hdb/Development/Debug/Adapter/Init.hs:(188,15)-(197,48)@+-- - @hdb/Development/Debug/Adapter/Proxy.hs:93:34-37@+srcSpanStringToSourceSpan :: String -> Either String SourceSpan+srcSpanStringToSourceSpan s = parseOnly pSrcSpan (T.pack s)+  where+    pSrcSpan = do+      fp <- pFile <* char ':'+      pParenStyle fp <|> pColonStyle fp++    -- file:(l1,c1)-(l2,c2)+    pParenStyle fp = do+      (l1, c1) <- (,) <$> (char '(' *> num <* char ',') <*> (num <* char ')') <* char '-'+      (l2, c2) <- (,) <$> (char '(' *> num <* char ',') <*> (num <* char ')')+      pure (SourceSpan fp l1 l2 c1 c2)++    -- file:l1:c1-c2+    pColonStyle fp = do+      l1 <- num <* char ':'+      c1 <- num <* char '-'+      c2 <- num+      pure (SourceSpan fp l1 l1 c1 c2)++    pFile :: Parser FilePath+    pFile = T.unpack <$> takeTill (== ':')++    num :: Parser Int+    num = decimal 
hdb/Development/Debug/Adapter/Breakpoints.hs view
@@ -21,11 +21,11 @@ commandBreakpointLocations :: DebugAdaptor () commandBreakpointLocations = do   BreakpointLocationsArguments{..} <- getArguments-  file <- fileFromSourcePath breakpointLocationsArgumentsSource+  filePath <- fileFromSourcePath breakpointLocationsArgumentsSource    DidGetBreakpoints mspan <-     sendSync $ GetBreakpointsAt-      ModuleBreak { path      = file+      ModuleBreak { path      = filePath                   , lineNum   = breakpointLocationsArgumentsLine                   , columnNum = breakpointLocationsArgumentsColumn                   }@@ -47,17 +47,17 @@ commandSetBreakpoints :: DebugAdaptor () commandSetBreakpoints = do   SetBreakpointsArguments {..} <- getArguments-  file <- fileFromSourcePath setBreakpointsArgumentsSource+  filePath <- fileFromSourcePath setBreakpointsArgumentsSource   let breaks_wanted = fromMaybe [] setBreakpointsArgumentsBreakpoints    -- Clear existing module breakpoints-  DidClearBreakpoints <- sendSync (ClearModBreakpoints file)+  DidClearBreakpoints <- sendSync (ClearModBreakpoints filePath)    -- Set requested ones   breaks <- forM breaks_wanted $ \bp -> do     DidSetBreakpoint bf <-       sendSync $ SetBreakpoint-        ModuleBreak { path      = file+        ModuleBreak { path      = filePath                     , lineNum   = DAP.sourceBreakpointLine bp                     , columnNum = DAP.sourceBreakpointColumn bp                     }@@ -81,7 +81,7 @@   breaks <- forM breaks_wanted $ \bp -> do     DidSetBreakpoint bf <-       sendSync $ SetBreakpoint-        FunctionBreak { function  = T.unpack $ fromJust {- not optional! -} $ DAP.functionBreakpointName bp }+        FunctionBreak { function  = T.unpack $ DAP.functionBreakpointName bp }         (readMaybe @Int =<< (T.unpack <$> DAP.functionBreakpointHitCondition bp))         (T.unpack <$> DAP.functionBreakpointCondition bp)     registerBreakFound bf
hdb/Development/Debug/Adapter/Evaluation.hs view
@@ -90,14 +90,15 @@     Output.stderr (T.pack resultVal)     sendTerminatedEvent defaultTerminatedEvent     sendExitedEvent (ExitedEvent 42)-  EvalStopped {breakId = Nothing} ->+  EvalStopped {breakId = Nothing, breakThread} ->     sendStoppedEvent       defaultStoppedEvent {         stoppedEventAllThreadsStopped = True       , stoppedEventReason = StoppedEventReasonException       , stoppedEventHitBreakpointIds = []+      , stoppedEventThreadId = Just $ remoteThreadIntRef breakThread       }-  EvalStopped {breakId = Just bid} -> do+  EvalStopped {breakId = Just bid, breakThread} -> do     DAS{breakpointMap} <- getDebugSession     sendStoppedEvent       defaultStoppedEvent {@@ -108,5 +109,6 @@                         else StoppedEventReasonBreakpoint       , stoppedEventHitBreakpointIds           = maybe [] IS.toList (M.lookup bid breakpointMap)+      , stoppedEventThreadId = Just $ remoteThreadIntRef breakThread       } 
hdb/Development/Debug/Adapter/Init.hs view
@@ -26,6 +26,7 @@ import Control.Monad.Catch import Control.Exception (SomeAsyncException, throwIO) import Control.Concurrent+import GHC.Conc.Sync (labelThread) import Control.Monad import Data.Aeson as Aeson import GHC.Generics@@ -34,7 +35,7 @@  import Development.Debug.Adapter import Development.Debug.Adapter.Exit-import GHC.Debugger.Logger+import GHC.Debugger.Logger as Logger import qualified Development.Debug.Adapter.Output as Output  import qualified GHC.Debugger as Debugger@@ -167,6 +168,8 @@ -- write to stdout, but always write to the appropiate handle. stdoutCaptureThread :: Bool -> Chan BS.ByteString -> (DebugAdaptorCont () -> IO ()) -> IO () stdoutCaptureThread runInTerminal syncOut withAdaptor = do+  tid <- myThreadId+  labelThread tid "Stdin Capture Thread"   withInterceptedStdout $ \_ interceptedStdout -> do     forever $ do       line <- liftIO $ T.hGetLine interceptedStdout@@ -179,6 +182,8 @@ -- | Like 'stdoutCaptureThread' but for stderr stderrCaptureThread :: Bool -> Chan BS.ByteString -> (DebugAdaptorCont () -> IO ()) -> IO () stderrCaptureThread runInTerminal syncErr withAdaptor = do+  tid <- myThreadId+  labelThread tid "Stderr Capture Thread"   withInterceptedStderr $ \_ interceptedStderr -> do     forever $ do       line <- liftIO $ T.hGetLine interceptedStderr@@ -198,6 +203,8 @@  stdinForwardThread :: Bool -> Chan BS.ByteString -> (DebugAdaptorCont () -> IO ()) -> IO () stdinForwardThread runInTerminal syncIn _withAdaptor = do+  tid <- myThreadId+  labelThread tid "Stdin Forward Thread"   when runInTerminal $ do     -- We need to hijack stdin to write to it @@ -257,7 +264,16 @@    catches     (do-      Debugger.runDebugger (cmapWithSev DebuggerMonadLog recorder) writeDebuggerOutput rootDir componentDir libdir units ghcInvocation extraGhcArgs mainFp runConf $ do+      -- The logger should log things starting from Info to the debugger output+      -- handle as well, so the user sees it in the Console.+      debugAdapterLogger <- handleLogger writeDebuggerOutput+      let final_logger = cmapWithSev DebuggerMonadLog recorder <>+                         applyVerbosity (Verbosity Logger.Info)+                          (cmap renderPrettyWithSeverity (fromCologAction debugAdapterLogger))+      Debugger.runDebugger final_logger writeDebuggerOutput rootDir componentDir libdir units ghcInvocation extraGhcArgs mainFp runConf $ do+        liftIO $ do+          tid <- myThreadId+          labelThread tid "Main Debugger Thread"         liftIO $ signalInitialized (Right ())         forever $ do           req <- takeMVar requests & liftIO
hdb/Development/Debug/Adapter/Proxy.hs view
@@ -17,6 +17,7 @@ import Control.Monad import Control.Monad.IO.Class import Control.Concurrent+import GHC.Conc.Sync (labelThread) import qualified Data.List.NonEmpty as NE  import qualified Data.Text as T@@ -63,6 +64,7 @@   port <- liftIO $ socketPort sock    _ <- liftIO $ forkIO $ ignoreIOException $ do+    myThreadId >>= \tid -> labelThread tid "Debug/Adapter/Proxy: TCP Server"     runTCPServerWithSocket sock $ \scket -> do        logWith l Info $ ProxyLog $ T.pack $ "Connected to client on port " ++ show port ++ "...!"@@ -70,6 +72,8 @@        -- -- Read stdout from chan and write to socket       _ <- forkIO $ ignoreIOException $ do+        tid <- myThreadId+        labelThread tid "Debug/Adapter/Proxy: Forward stdout"         forever $ do           bs <- readChan dbOut           logWith l Debug $ ProxyLog $ T.pack $ "Writing to socket: " ++ BS8.unpack bs@@ -77,6 +81,8 @@        -- Read stderr from chan and write to socket       _ <- forkIO $ ignoreIOException $ do+        tid <- myThreadId+        labelThread tid "Debug/Adapter/Proxy: Forward stderr"         forever $ do           bs <- readChan dbErr           logWith l Debug $ ProxyLog $ T.pack $ "Writing to socket (from stderr): " ++ BS8.unpack bs@@ -124,7 +130,7 @@         catch (forever $ do           str <- BS8.hGetLine stdin           NBS.sendAll sock (str <> BS8.pack "\n")-          ) $ \(e::IOException) -> return () -- connection dropped, just exit.+          ) $ \(_e::IOException) -> return () -- connection dropped, just exit.        -- Forward stdout from sock       catch (forever $ do@@ -135,9 +141,9 @@             close sock             exitSuccess           else BS8.hPut stdout msg >> hFlush stdout-        ) $ \(e::IOException) -> return () -- connection dropped, just exit.+        ) $ \(_e::IOException) -> return () -- connection dropped, just exit. -    ) $ \(e::IOException) -> do+    ) $ \(_e::IOException) -> do       hPutStrLn stderr "Failed to connect to debugger server proxy -- did the debuggee compile and start running successfully?"  -- | Send a 'runInTerminal' reverse request to the DAP client
hdb/Development/Debug/Adapter/Stopped.hs view
@@ -14,6 +14,7 @@ -- @ module Development.Debug.Adapter.Stopped where +import Control.Monad import qualified Data.Text as T  import DAP@@ -28,13 +29,15 @@  -- | Command to get thread information at current stopped point commandThreads :: DebugAdaptor ()-commandThreads = do -- TODO-  sendThreadsResponse [+commandThreads = do+  GotThreads ts <- sendSync GetThreads+  sendThreadsResponse $+    map (\t ->       Thread-        { threadId    = 0-        , threadName  = T.pack "dummy thread"+        { threadId    = remoteThreadIntRef t.tId+        , threadName  = maybe (T.pack $ "Thread #" ++ show (remoteThreadIntRef t.tId)) T.pack t.tName         }-    ]+      ) ts  -------------------------------------------------------------------------------- -- * StackTrace@@ -44,29 +47,28 @@ commandStackTrace :: DebugAdaptor () commandStackTrace = do   StackTraceArguments{..} <- getArguments-  GotStacktrace fs <- sendSync GetStacktrace-  case fs of-    []  ->-      -- No frames; should be stopped on exception-      sendStackTraceResponse StackTraceResponse { stackFrames = [], totalFrames = Nothing }-    [f] -> do-      source <- fileToSource f.sourceSpan.file-      let-        topStackFrame = defaultStackFrame-          { stackFrameId = 0-          , stackFrameName = T.pack f.name-          , stackFrameLine = f.sourceSpan.startLine-          , stackFrameColumn = f.sourceSpan.startCol-          , stackFrameEndLine = Just f.sourceSpan.endLine-          , stackFrameEndColumn = Just f.sourceSpan.endCol-          , stackFrameSource = Just source-          }-      sendStackTraceResponse StackTraceResponse-        { stackFrames = [topStackFrame]-        , totalFrames = Just 1-        }-    _ -> error $ "Unexpected multiple frames since implementation doesn't support it yet: " ++ show fs+  GotStacktrace stackFrames <- sendSync (GetStacktrace (RemoteThreadId stackTraceArgumentsThreadId))+  responseFrames <- forM (zip stackFrames [1..]) $ \(stackFrame, stackFrameIx) -> do+    source <- fileToSource stackFrame.sourceSpan.file+    return defaultStackFrame+      { stackFrameId = stackTraceArgumentsThreadId*1000000 + stackFrameIx+      , stackFrameName = T.pack stackFrame.name+      , stackFrameLine = stackFrame.sourceSpan.startLine+      , stackFrameColumn = stackFrame.sourceSpan.startCol+      , stackFrameEndLine = Just stackFrame.sourceSpan.endLine+      , stackFrameEndColumn = Just stackFrame.sourceSpan.endCol+      , stackFrameSource = Just source+      }+  sendStackTraceResponse StackTraceResponse+    { stackFrames = responseFrames+    , totalFrames = if null responseFrames then Nothing else Just (length responseFrames) } +-- | Come up with a unique identifier for a stack frame (only valid while in+-- this stopped environment, see "Lifetime of Objects References" in DAP+-- overview).+--+-- The unique identifier is based on the threadId and index of the stack frame for this thread's stack.+-- mkUniqueStackId ::  -------------------------------------------------------------------------------- -- * Scopes@@ -75,7 +77,7 @@ -- | Command to get scopes for current stopped point commandScopes :: DebugAdaptor () commandScopes = do-  ScopesArguments{scopesArgumentsFrameId=0} <- getArguments+  ScopesArguments{scopesArgumentsFrameId=_IGNORED_BUT_NOW_WE_HAVE_TO_CARE} <- getArguments   GotScopes scopes <- sendSync GetScopes   sendScopesResponse . ScopesResponse =<<     mapM scopeInfoToScope scopes
hdb/Development/Debug/Interactive.hs view
@@ -5,14 +5,14 @@ import System.Exit import System.Directory import System.Console.Haskeline-import System.Console.Haskeline.Completion+-- import System.Console.Haskeline.Completion import System.FilePath import Control.Monad.Except import Control.Monad.State import Control.Monad.Reader import Control.Monad.RWS import Options.Applicative-import Options.Applicative.BashCompletion+-- import Options.Applicative.BashCompletion  import Development.Debug.Session.Setup @@ -122,14 +122,15 @@   DidContinue er -> outputStrLn $ show er   DidStep er -> printEvalResult recd er   DidExec er -> outputStrLn $ show er+  GotThreads threads -> outputStrLn $ show threads   GotStacktrace stackframes -> outputStrLn $ show stackframes   GotScopes scopeinfos -> outputStrLn $ show scopeinfos   GotVariables vis -> outputStrLn $ show vis -- (Either VarInfo [VarInfo])-  Aborted str -> outputStrLn ("Aborted: " ++ str)+  Aborted err_str -> outputStrLn ("Aborted: " ++ err_str)   Initialised -> pure ()  printEvalResult :: Recorder (WithSeverity DebuggerLog) -> EvalResult -> InteractiveDM ()-printEvalResult recd EvalStopped{breakId} = do+printEvalResult recd EvalStopped{breakId=_} = do   out <- lift . lift $ execute recd GetScopes   printResponse recd out printEvalResult _ er = outputStrLn $ show er@@ -191,7 +192,7 @@   -- just "run"   <|> (pure $ DebugExecution (mkEntry entryPoint) entryFile entryArgs)   where-    parseEntry =+    _parseEntry =       fmap mkEntry $       option str         ( long "entry"@@ -269,4 +270,5 @@        in outputStrLn msg >> pure Nothing     _ -> outputStrLn "Unsupported command parsing mode" >> pure Nothing +parserPrefs :: ParserPrefs parserPrefs = prefs (disambiguate <> showHelpOnError <> showHelpOnEmpty)
hdb/Main.hs view
@@ -176,7 +176,7 @@      -- connection is expected. See #95.      -> Command -> DebugAdaptor () ---------------------------------------------------------------------------------talk l support_rit_var pid_var client_proxy_signal = \ case+talk l support_rit_var _pid_var client_proxy_signal = \ case   CommandInitialize -> do     InitializeRequestArguments{supportsRunInTerminalRequest} <- getArguments     let runInTerminal = fromMaybe False supportsRunInTerminalRequest@@ -271,7 +271,7 @@     -> IORef (Maybe Int)     -- ^ Reference to PID of runInTerminal proxy process running     -> ReverseRequestResponse -> DebugAdaptorCont ()-ack l ref rrr = case rrr.reverseRequestCommand of+ack l _ref rrr = case rrr.reverseRequestCommand of   ReverseCommandRunInTerminal -> do     when rrr.success $ do       logWith l Info $ LaunchLog $ T.pack "RunInTerminal was successful"
test/haskell/Main.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE LambdaCase, OverloadedStrings, ViewPatterns, QuasiQuotes #-}+{-# LANGUAGE LambdaCase, OverloadedStrings, ViewPatterns, QuasiQuotes, CPP #-} module Main (main) where  import Text.RE.TDFA.Text.Lazy@@ -15,6 +15,7 @@ import Control.Exception  import Test.Tasty+import Test.Tasty.ExpectedFailure import Test.Tasty.Golden as G import Test.Tasty.Golden.Advanced as G @@ -25,11 +26,15 @@ main = do   goldens <- mapM (mkGoldenTest False) =<< findByExtension [".hdb-test"] "test/golden"   defaultMain $+#ifdef mingw32_HOST_OS+    ignoreTestBecause "Testsuite is not enabled on Windows (#149)" $+#endif     testGroup "Tests"       [ testGroup "Golden tests" goldens       , testGroup "Unit tests" unitTests       ] +unitTests :: [TestTree] unitTests =   [ runInTerminalTests   ]@@ -77,10 +82,14 @@   -- Normalise the action producing the output   normalisingAct = do     tmpDir <- getCanonicalTemporaryDirectory-    replaceRE <- compileSearchReplace (tmpDir ++ ".*" ++ takeBaseName (takeDirectory ref) {- the folder in which the test is run, inside the canonical temp dir-})-                                      "<TEMPORARY-DIRECTORY>"+    replaceREs <- traverse (uncurry compileSearchReplace)+      [ ( tmpDir ++ ".*" ++ takeBaseName (takeDirectory ref) {- the folder in which the test is run, inside the canonical temp dir-}+        , "<TEMPORARY-DIRECTORY>" )+      , ( "Using cabal specification: .*"+        , "Using cabal specification: <VERSION>" )+      ] -    let normalising (LT.decodeUtf8 -> txt) = txt *=~/ replaceRE+    let normalising (LT.decodeUtf8 -> txt) = foldl' (*=~/) txt replaceREs      normalising <$> act 
test/haskell/Test/DAP/RunInTerminal.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE CPP #-} module Test.DAP.RunInTerminal (runInTerminalTests) where  import Control.Concurrent@@ -17,6 +18,7 @@ import Test.DAP import Test.Tasty import Test.Tasty.HUnit+import Test.Tasty.ExpectedFailure import Test.Utils import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy.Char8 as LB8@@ -26,7 +28,11 @@  runInTerminalTests =   testGroup "DAP.RunInTerminal"-    [ testCase "runInTerminal: proxy forwards stdin correctly" runInTerminal1+    [+#ifdef mingw32_HOST_OS+      ignoreTestBecause "Needs to be fixed for Windows" $+#endif+      testCase "runInTerminal: proxy forwards stdin correctly" runInTerminal1     ]  rit_keep_tmp_dirs :: Bool