packages feed

ghc-stack-profiler-0.1.0.0: src/GHC/Internal/Stack/Decode/Compat.hs

{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE GHCForeignImportPrim #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE CPP #-}
module GHC.Internal.Stack.Decode.Compat (
  StackFrameLocation,
  StackSnapshot#,
  StackInfoTable(..),
  getInfoTableForStack,
  getInfoTableOnStack,
  advanceStackFrameLocation,
  stackHead,
  Box(..),
  getClosureBox,
) where

import GHC.Exts
import GHC.Stack.CloneStack (StackSnapshot (..))
-- See Note [No way-dependent imports]
#if defined(PROFILING)
import GHC.Exts.Heap.InfoTableProf
#else
import GHC.Exts.Heap.InfoTable
#endif
import qualified GHC.Internal.InfoProv.Types as InfoProv
import GHC.Internal.Heap.Closures.Compat
import GHC.Internal.Stack.Constants.Compat (WordOffset)

{-
Note [No way-dependent imports]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`ghc -M` currently assumes that the imports for a module are the same
in every way.  This is arguably a bug, but breaking this assumption by
importing different things in different ways can cause trouble.  For
example, this module in the profiling way imports and uses
GHC.Exts.Heap.InfoTableProf.  When it was not also imported in the
vanilla way, there were intermittent build failures due to this module
being compiled in the profiling way before GHC.Exts.Heap.InfoTableProf
in the profiling way. (#15197)
-}

type StackFrameLocation = (StackSnapshot, WordOffset)

data StackInfoTable = StackInfoTable
  { infoTableStructPtr :: Ptr InfoProv.StgInfoTable
  , infoTablePtr :: Ptr InfoProv.StgInfoTable
  , infoTable :: StgInfoTable
  }

-- | Get the 'StgInfoTable' of the stack frame.
-- Additionally, provides 'InfoProv' for the 'StgInfoTable' if there is any.
getInfoTableOnStack :: StackSnapshot# -> WordOffset -> IO StackInfoTable
getInfoTableOnStack stackSnapshot# index = do
  let !(# itbl_struct#, itbl_ptr_ipe_key# #) = getInfoTableAddrs# stackSnapshot# (wordOffsetToWord# index)
      itbl_struct = Ptr itbl_struct#
      itbl_ptr = Ptr itbl_ptr_ipe_key#

  itbl <- peekItbl itbl_struct
  pure StackInfoTable
    { infoTableStructPtr = itbl_struct
    , infoTablePtr = itbl_ptr
    , infoTable = itbl
    }

getInfoTableForStack :: StackSnapshot# -> IO StgInfoTable
getInfoTableForStack stackSnapshot# =
  peekItbl $
    Ptr (getStackInfoTableAddr# stackSnapshot#)

-- | Advance to the next stack frame (if any)
advanceStackFrameLocation :: StackFrameLocation -> Maybe StackFrameLocation
advanceStackFrameLocation (StackSnapshot stackSnapshot#, index) =
  let !(# s', i', hasNext #) = advanceStackFrameLocation# stackSnapshot# (wordOffsetToWord# index)
   in if I# hasNext > 0
        then Just (StackSnapshot s', primWordToWordOffset i')
        else Nothing
  where
    primWordToWordOffset :: Word# -> WordOffset
    primWordToWordOffset w# = fromIntegral (W# w#)

-- | `StackFrameLocation` of the top-most stack frame
stackHead :: StackSnapshot# -> StackFrameLocation
stackHead s# = (StackSnapshot s#, 0) -- GHC stacks are never empty

getClosureBox :: StackSnapshot# -> WordOffset -> Box
getClosureBox stackSnapshot# index =
  case getStackClosure# stackSnapshot# (wordOffsetToWord# index) of
    -- c needs to be strictly evaluated, otherwise a thunk gets boxed (and
    -- will later be decoded as such)
    !c -> Box c

-- | Advance to the next stack frame (if any)
--
-- The last `Int#` in the result tuple is meant to be treated as bool
-- (has_next).
foreign import prim "advanceStackFrameLocationzh"
  advanceStackFrameLocation# :: StackSnapshot# -> Word# -> (# StackSnapshot#, Word#, Int# #)

foreign import prim "getInfoTableAddrszh"
  getInfoTableAddrs# :: StackSnapshot# -> Word# -> (# Addr#, Addr# #)

foreign import prim "getStackInfoTableAddrzh"
  getStackInfoTableAddr# :: StackSnapshot# -> Addr#

foreign import prim "getStackClosurezh"
  getStackClosure# :: StackSnapshot# -> Word# ->  Any

-- ----------------------------------------------------------------------------
-- Utilities that really should live somewhere else
-- ----------------------------------------------------------------------------

-- | Unbox 'Int#' from 'Int'
toInt# :: Int -> Int#
toInt# (I# i) = i

-- | Convert `Int` to `Word#`
intToWord# :: Int -> Word#
intToWord# i = int2Word# (toInt# i)

wordOffsetToWord# :: WordOffset -> Word#
wordOffsetToWord# wo = intToWord# (fromIntegral wo)