@@ -21,6 +21,7 @@ import GHC.Driver.Env as GHC
2121import GHC.Runtime.Eval
2222import GHC.Types.SrcLoc
2323import GHC.InfoProv
24+ import GHC.Data.FastString (unpackFS )
2425import GHC.Utils.Outputable as Ppr
2526import qualified GHC.Unit.Home.Graph as HUG
2627
@@ -34,6 +35,10 @@ import GHC.Debugger.Interface.Messages
3435import qualified GHC.Debugger.Interface.Messages as DbgStackFrame (DbgStackFrame (.. ))
3536import GHC.Debugger.Utils
3637import qualified GHC.Debugger.Logger as Logger
38+ import qualified GHC.Debugger.Runtime.Eval.RemoteExpr as Remote
39+ import GHC.Debugger.Runtime.Term.Parser
40+ import GHCi.RemoteTypes (castForeignRef )
41+ import GHC.Builtin.Types (anyTy )
3742
3843{-
3944Note [Don't crash if not stopped]
@@ -154,31 +159,36 @@ getStacktrace req_tid = do
154159 [] ->
155160 -- See Note [Don't crash if not stopped]
156161 return Nothing
157- r: _
158- | Just ss <- realSrcSpanToSourceSpan <$> srcSpanToRealSrcSpan (GHC. resumeSpan r)
159- , Just ss /= (fmap DbgStackFrame. sourceSpan (listToMaybe decoded_frames))
160- -- don't include the resume context entry if it is already at the
161- -- start of the decoded frames
162- , Just ibi <- GHC. resumeBreakpointId r
163- -> do
164- r_tid <- getRemoteThreadIdFromRemoteContext (GHC. resumeContext r)
165- if r_tid == req_tid then do
166- -- We're getting the stacktrace for the thread we're stopped at.
167- info_brks <- liftIO $ readIModBreaks hug ibi
168- let modl = getBreakSourceMod ibi info_brks
169- modl_str <- display modl
170- return $
171- Just DbgStackFrame
172- { name = modl_str ++ " ." ++ GHC. resumeDecl r
173- , sourceSpan = ss
174- , breakId = Just ibi
175- }
176- else
177- return Nothing
178- | otherwise ->
179- -- No resume span; which should mean we're stopped on an exception.
180- -- No info for now.
181- return Nothing
162+ r: _ -> do
163+ let resumeSpanR = GHC. resumeSpan r
164+ mRealSpan = realSrcSpanToSourceSpan <$> srcSpanToRealSrcSpan resumeSpanR
165+ firstSpan = DbgStackFrame. sourceSpan <$> listToMaybe decoded_frames
166+ r_tid <- getRemoteThreadIdFromRemoteContext (GHC. resumeContext r)
167+ if r_tid /= req_tid then
168+ return Nothing
169+ else case GHC. resumeBreakpointId r of
170+ Just ibi
171+ | Just ss <- mRealSpan
172+ , Just ss /= firstSpan -> do
173+ -- We're getting the stacktrace for the thread we're stopped at.
174+ info_brks <- liftIO $ readIModBreaks hug ibi
175+ let modl = getBreakSourceMod ibi info_brks
176+ modl_str <- display modl
177+ return $
178+ Just DbgStackFrame
179+ { name = modl_str ++ " ." ++ GHC. resumeDecl r
180+ , sourceSpan = ss
181+ , breakId = Just ibi
182+ }
183+ _ -> do
184+ mExcSpan <- exceptionSourceSpanFromContext
185+ case mExcSpan of
186+ Just sourceSpan -> return $ Just DbgStackFrame
187+ { name = GHC. resumeDecl r
188+ , sourceSpan
189+ , breakId = Nothing
190+ }
191+ Nothing -> return Nothing
182192 return (maybe id (:) head_frame $ decoded_frames)
183193
184194--------------------------------------------------------------------------------
@@ -191,6 +201,12 @@ getScopes threadId frameIx = do
191201 frames <- getStacktrace threadId
192202 let frame = frames !! frameIx
193203 let sourceSpan = DbgStackFrame. sourceSpan frame
204+ localsScope = ScopeInfo
205+ { kind = LocalVariablesScope
206+ , expensive = False
207+ , numVars = Nothing
208+ , sourceSpan
209+ }
194210 if
195211 | frameIx < length frames
196212 , Just ibi <- DbgStackFrame. breakId frame
@@ -204,11 +220,7 @@ getScopes threadId frameIx = do
204220 in_mod <- getTopEnv brk_modl
205221 imported <- getTopImported brk_modl
206222 return
207- [ ScopeInfo { kind = LocalVariablesScope
208- , expensive = False
209- , numVars = Nothing
210- , sourceSpan
211- }
223+ [ localsScope
212224 , ScopeInfo { kind = ModuleVariablesScope
213225 , expensive = True
214226 , numVars = Just (sizeUFM in_mod)
@@ -221,7 +233,7 @@ getScopes threadId frameIx = do
221233 }
222234 ]
223235 | otherwise ->
224- return []
236+ return [localsScope ]
225237
226238--------------------------------------------------------------------------------
227239-- * Variables
@@ -354,3 +366,79 @@ getTopImported modl = do
354366 liftIO $ HUG. lookupHugByModule modl (hsc_HUG hsc_env) >>= \ case
355367 Nothing -> return emptyGlobalRdrEnv
356368 Just hmi -> mkTopLevImportedEnv hsc_env hmi
369+
370+ --------------------------------------------------------------------------------
371+ -- * Exception context helpers
372+ --------------------------------------------------------------------------------
373+
374+ exceptionSourceSpanFromContext :: Debugger (Maybe SourceSpan )
375+ exceptionSourceSpanFromContext = do
376+ GHC. getResumeContext >>= \ case
377+ r: _ | resumeHistoryIx r == 0
378+ , Nothing <- GHC. resumeBreakpointId r -> do
379+ let excRef = resumeApStack r
380+ evalRes <- Remote. eval
381+ (Remote. raw exceptionLocationExpr `Remote.app` Remote. untypedRef excRef)
382+ case evalRes of
383+ Left err -> do
384+ logSDoc Logger. Debug $
385+ Ppr. text " Failed to evaluate exception context:" Ppr. <+> Ppr. text (show err)
386+ return Nothing
387+ Right fhv -> do
388+ parsed <- obtainParsedTerm " Exception context location" 4 True anyTy (castForeignRef fhv)
389+ (maybeParser exceptionLocationTupleParser)
390+ case parsed of
391+ Left errs -> do
392+ logSDoc Logger. Debug $
393+ Ppr. text " Failed to parse exception context location:"
394+ Ppr. <+> Ppr. vcat (map (Ppr. text . getTermErrorMessage) errs)
395+ return Nothing
396+ Right Nothing -> return Nothing
397+ Right (Just (file, line, col)) ->
398+ return $ Just SourceSpan
399+ { file = file
400+ , startLine = line
401+ , startCol = col
402+ , endLine = line
403+ , endCol = col
404+ }
405+ _ -> return Nothing
406+
407+ exceptionLocationTupleParser :: TermParser (String , Int , Int )
408+ exceptionLocationTupleParser =
409+ (,,) <$> subtermWith 0 stringParser
410+ <*> subtermWith 1 intParser
411+ <*> subtermWith 2 intParser
412+
413+ exceptionLocationExpr :: String
414+ exceptionLocationExpr = unlines
415+ [ " let"
416+ , " fromCallStack cs = case GHC.Internal.Data.Maybe.listToMaybe (GHC.Internal.Stack.getCallStack cs) of"
417+ , " Just (_, loc) -> Just ( GHC.Internal.Stack.Types.srcLocFile loc"
418+ , " , GHC.Internal.Stack.Types.srcLocStartLine loc"
419+ , " , GHC.Internal.Stack.Types.srcLocStartCol loc)"
420+ , " go exc ="
421+ , " let ctx = GHC.Internal.Exception.Type.someExceptionContext exc"
422+ , " bts :: [GHC.Internal.Exception.Backtrace.Backtraces]"
423+ , " bts = Control.Exception.Context.getExceptionAnnotations ctx"
424+ , " in case bts of"
425+ , " bt : _ -> case GHC.Internal.Exception.Backtrace.btrHasCallStack bt of"
426+ , " Just cs -> fromCallStack cs"
427+ , " Nothing -> Nothing"
428+ , " [] -> Nothing"
429+ , " in go"
430+ ]
431+
432+ fallbackExceptionSourceSpan :: Maybe SrcSpan -> SourceSpan
433+ fallbackExceptionSourceSpan mspan =
434+ let fileLabel = maybe " <exception>" spanLabel mspan
435+ in SourceSpan
436+ { file = fileLabel
437+ , startLine = 0
438+ , startCol = 0
439+ , endLine = 0
440+ , endCol = 0
441+ }
442+ where
443+ spanLabel (RealSrcSpan rss _) = unpackFS (srcSpanFile rss)
444+ spanLabel (UnhelpfulSpan reason) = unpackFS (unhelpfulSpanFS reason)
0 commit comments