Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion haskell-debugger-view/src/GHC/Debugger/View/Class.hs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module GHC.Debugger.View.Class

import Data.Int
import Data.Word
import Control.Exception

-- | Custom handling of debug terms (e.g. in the variables pane, or when
-- inspecting a lazy variable)
Expand Down Expand Up @@ -180,6 +181,15 @@ instance DebugView (a, b) where
[ ("fst", VarFieldValue x)
, ("snd", VarFieldValue y) ]

instance DebugView SomeException where
debugValue e = simpleValue (displayException e) True
debugFields e@(SomeException exc) =
let !ctx = someExceptionContext e
in pure $ VarFields
[ ("exception", VarFieldValue exc)
, ("context", VarFieldValue ctx)
]
Comment thread
mpickering marked this conversation as resolved.

-- | This instance will display up to the first 50 forced elements of a list.
instance {-# OVERLAPPABLE #-} DebugView [a] where
debugValue [] = simpleValue "[]" False
Expand Down Expand Up @@ -219,4 +229,3 @@ toVarFieldsIO :: VarFields -> [(IO String, VarFieldValue)]
toVarFieldsIO x =
case x of
VarFields fls -> [ (pure fl_s, b) | (fl_s, b) <- fls]

2 changes: 1 addition & 1 deletion haskell-debugger.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ executable hdb
network-run >= 0.4.4,
async >= 2.2.5 && < 2.3,
text >= 2.1 && < 2.3,
dap >= 0.3.1 && < 0.4,
dap >= 0.4 && < 0.5,

haskeline >= 0.8 && < 1,
optparse-applicative >= 0.18 && < 0.20
Expand Down
150 changes: 119 additions & 31 deletions haskell-debugger/GHC/Debugger/Stopped.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import GHC.Driver.Env as GHC
import GHC.Runtime.Eval
import GHC.Types.SrcLoc
import GHC.InfoProv
import GHC.Data.FastString (unpackFS)
import GHC.Utils.Outputable as Ppr
import qualified GHC.Unit.Home.Graph as HUG

Expand All @@ -34,6 +35,10 @@ import GHC.Debugger.Interface.Messages
import qualified GHC.Debugger.Interface.Messages as DbgStackFrame (DbgStackFrame(..))
import GHC.Debugger.Utils
import qualified GHC.Debugger.Logger as Logger
import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote
import GHC.Debugger.Runtime.Term.Parser
import GHCi.RemoteTypes (castForeignRef)
import GHC.Builtin.Types (anyTy)

{-
Note [Don't crash if not stopped]
Expand Down Expand Up @@ -154,31 +159,36 @@ getStacktrace req_tid = do
[] ->
-- See Note [Don't crash if not stopped]
return Nothing
r:_
| Just ss <- realSrcSpanToSourceSpan <$> srcSpanToRealSrcSpan (GHC.resumeSpan r)
, Just ss /= (fmap DbgStackFrame.sourceSpan (listToMaybe decoded_frames))
-- don't include the resume context entry if it is already at the
-- start of the decoded frames
, Just ibi <- GHC.resumeBreakpointId r
-> do
r_tid <- getRemoteThreadIdFromRemoteContext (GHC.resumeContext r)
if r_tid == req_tid then do
-- We're getting the stacktrace for the thread we're stopped at.
info_brks <- liftIO $ readIModBreaks hug ibi
let modl = getBreakSourceMod ibi info_brks
modl_str <- display modl
return $
Just DbgStackFrame
{ name = modl_str ++ "." ++ GHC.resumeDecl r
, sourceSpan = ss
, breakId = Just ibi
}
else
return Nothing
| otherwise ->
-- No resume span; which should mean we're stopped on an exception.
-- No info for now.
return Nothing
r:_ -> do
let resumeSpanR = GHC.resumeSpan r
mRealSpan = realSrcSpanToSourceSpan <$> srcSpanToRealSrcSpan resumeSpanR
firstSpan = DbgStackFrame.sourceSpan <$> listToMaybe decoded_frames
r_tid <- getRemoteThreadIdFromRemoteContext (GHC.resumeContext r)
if r_tid /= req_tid then
return Nothing
else case GHC.resumeBreakpointId r of
Just ibi
| Just ss <- mRealSpan
, Just ss /= firstSpan -> do
-- We're getting the stacktrace for the thread we're stopped at.
info_brks <- liftIO $ readIModBreaks hug ibi
let modl = getBreakSourceMod ibi info_brks
modl_str <- display modl
return $
Just DbgStackFrame
{ name = modl_str ++ "." ++ GHC.resumeDecl r
, sourceSpan = ss
, breakId = Just ibi
}
_ -> do
mExcSpan <- exceptionSourceSpanFromContext
case mExcSpan of
Just sourceSpan -> return $ Just DbgStackFrame
{ name = GHC.resumeDecl r
, sourceSpan
, breakId = Nothing
}
Nothing -> return Nothing
return (maybe id (:) head_frame $ decoded_frames)

--------------------------------------------------------------------------------
Expand All @@ -191,6 +201,12 @@ getScopes threadId frameIx = do
frames <- getStacktrace threadId
let frame = frames !! frameIx
let sourceSpan = DbgStackFrame.sourceSpan frame
localsScope = ScopeInfo
{ kind = LocalVariablesScope
, expensive = False
, numVars = Nothing
, sourceSpan
}
if
| frameIx < length frames
, Just ibi <- DbgStackFrame.breakId frame
Expand All @@ -204,11 +220,7 @@ getScopes threadId frameIx = do
in_mod <- getTopEnv brk_modl
imported <- getTopImported brk_modl
return
[ ScopeInfo { kind = LocalVariablesScope
, expensive = False
, numVars = Nothing
, sourceSpan
}
[ localsScope
, ScopeInfo { kind = ModuleVariablesScope
, expensive = True
, numVars = Just (sizeUFM in_mod)
Expand All @@ -221,7 +233,7 @@ getScopes threadId frameIx = do
}
]
| otherwise ->
return []
return [localsScope]

--------------------------------------------------------------------------------
-- * Variables
Expand Down Expand Up @@ -354,3 +366,79 @@ getTopImported modl = do
liftIO $ HUG.lookupHugByModule modl (hsc_HUG hsc_env) >>= \case
Nothing -> return emptyGlobalRdrEnv
Just hmi -> mkTopLevImportedEnv hsc_env hmi

--------------------------------------------------------------------------------
-- * Exception context helpers
--------------------------------------------------------------------------------

exceptionSourceSpanFromContext :: Debugger (Maybe SourceSpan)
exceptionSourceSpanFromContext = do
GHC.getResumeContext >>= \case
r:_ | resumeHistoryIx r == 0
, Nothing <- GHC.resumeBreakpointId r -> do
let excRef = resumeApStack r
evalRes <- Remote.eval
(Remote.raw exceptionLocationExpr `Remote.app` Remote.untypedRef excRef)
case evalRes of
Left err -> do
logSDoc Logger.Debug $
Ppr.text "Failed to evaluate exception context:" Ppr.<+> Ppr.text (show err)
return Nothing
Right fhv -> do
parsed <- obtainParsedTerm "Exception context location" 4 True anyTy (castForeignRef fhv)
(maybeParser exceptionLocationTupleParser)
case parsed of
Left errs -> do
logSDoc Logger.Debug $
Ppr.text "Failed to parse exception context location:"
Ppr.<+> Ppr.vcat (map (Ppr.text . getTermErrorMessage) errs)
return Nothing
Right Nothing -> return Nothing
Right (Just (file, line, col)) ->
return $ Just SourceSpan
{ file = file
, startLine = line
, startCol = col
, endLine = line
, endCol = col
}
_ -> return Nothing

exceptionLocationTupleParser :: TermParser (String, Int, Int)
exceptionLocationTupleParser =
(,,) <$> subtermWith 0 stringParser
<*> subtermWith 1 intParser
<*> subtermWith 2 intParser

exceptionLocationExpr :: String
exceptionLocationExpr = unlines
[ "let"
, " fromCallStack cs = case GHC.Internal.Data.Maybe.listToMaybe (GHC.Internal.Stack.getCallStack cs) of"
, " Just (_, loc) -> Just ( GHC.Internal.Stack.Types.srcLocFile loc"
, " , GHC.Internal.Stack.Types.srcLocStartLine loc"
, " , GHC.Internal.Stack.Types.srcLocStartCol loc)"
, " go exc ="
, " let ctx = GHC.Internal.Exception.Type.someExceptionContext exc"
, " bts :: [GHC.Internal.Exception.Backtrace.Backtraces]"
, " bts = Control.Exception.Context.getExceptionAnnotations ctx"
, " in case bts of"
, " bt : _ -> case GHC.Internal.Exception.Backtrace.btrHasCallStack bt of"
, " Just cs -> fromCallStack cs"
, " Nothing -> Nothing"
, " [] -> Nothing"
, " in go"
]

Comment thread
mpickering marked this conversation as resolved.
Outdated
fallbackExceptionSourceSpan :: Maybe SrcSpan -> SourceSpan
fallbackExceptionSourceSpan mspan =
let fileLabel = maybe "<exception>" spanLabel mspan
in SourceSpan
{ file = fileLabel
, startLine = 0
, startCol = 0
, endLine = 0
, endCol = 0
}
where
spanLabel (RealSrcSpan rss _) = unpackFS (srcSpanFile rss)
spanLabel (UnhelpfulSpan reason) = unpackFS (unhelpfulSpanFS reason)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the busy work, but could you move this to a new module GHC.Debugger.Stopped.Exception

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.