child process statistics

Dean Herington heringto@cs.unc.edu
Thu, 27 Feb 2003 23:35:38 -0500 (EST)


On Wed, 26 Feb 2003, Sigbjorn Finne wrote:

> wait3() or getrusage()? (Neither of which are supported 
> by the posix library.)
> 
> --sigbjorn

Thanks, Sigbjorn.  I drafted a Haskell wrapping of wait4(), modeled on
code I found in CVS.  See attached. I'd like a bit of help in two 
respects:

1. I found it difficult to satisfy the code requirements because I'm
building the module outside the RTS build environment.  Some issues:

 a. I don't know how to make a .hsc file, which would be the appropriate
form for the file, I think.  This meant I couldn't use #const, and may
also explain why I couldn't find the __hsunix_* routines.

 b. I had to duplicate some helper functions, including `waitOptions` and
`decipherWaitStatus`, because they didn't seem to be exported from the
module in which they're defined.

 c. I'm not sure how to define `struct timeval` and `struct rusage` so I
can avoid the kludgy code in `makeProcessResourceUsage`.

It seems that maybe I should be building in the RTS environment (and
maybe even extending the System.Posix.Process module itself) to clean
up all of the above problems.  That seems a bit daunting to me, however.

2. Having gotten my module to compile cleanly, it--not surprisingly--fails
at runtime:

invalid argument
Action: reapChildProcess
Reason: Invalid argument

Maybe a few minutes of your eyes (or those of anyone else who has delved
in the RTS) would spot the problem(s) quickly.  I tried gdb and saw what
appear to me to be reasonable arguments to wait4(), but I'm over my head
here.

Thanks in advance.

Dean


> ----- Original Message ----- 
> From: "Dean Herington" <heringto@cs.unc.edu>
> To: <glasgow-haskell-users@haskell.org>
> Sent: Wednesday, February 26, 2003 16:17
> Subject: child process statistics
> 
> 
> > Does anyone know how to get the process statistics (cputime, page faults,
> > etc.) for a child process?  When the child process has terminated, before
> > doing getAnyProcessStatus on it, I'd like to get its statistics.
> > 
> > Browsing the libraries documentation didn't turn anything up.  I couldn't
> > even find what I want at the Unix level among man pages.  (I'm happy to
> > write some FFI code to access system routines if I can locate the right
> > ones.)
> > 
> > Dean



{-# OPTIONS -fglasgow-exts #-}

-- Posix extras

-- $Id$

module PosixExtras
(
  ProcessResourceUsage(..),
  reapChildProcess
)
where

import Ratio
import System
import Posix hiding (userTime, systemTime)
import System.IO.Error
import Foreign
import Foreign.C


data ProcessResourceUsage =
     ProcessResourceUsage { userTime, systemTime :: Rational }
  deriving (Show, Read)


reapChildProcess :: Bool -> Bool -> ProcessID
        -> IO (Maybe (ProcessID, ProcessStatus, ProcessResourceUsage))
reapChildProcess block stopped pid =
  alloca $ \p_wstat ->
  allocaBytes structRusageSize $ \p_sru -> do
    pid' <- throwErrnoIfMinus1 "reapChildProcess"
              (c_wait4 (fromIntegral pid) p_wstat
                       (waitOptions block stopped) p_sru)
    case pid' of
      0 -> return Nothing
      _ -> do ps  <- decipherWaitStatus p_wstat
              pru <- makeProcessResourceUsage p_sru
              return (Just (fromIntegral pid', ps, pru))


type CPid = CInt

structRusageSize = 144  -- I think it's 72.  Include 100% slop.

type CRusage = CLong  -- cheat

makeProcessResourceUsage :: Ptr CLong -> IO ProcessResourceUsage
makeProcessResourceUsage p_sru = do
  -- Assume that ru_utime and ru_stime are at the beginning of struct rusage.
  [uhi, ulo, shi, slo] <- mapM get [0..3]
  let u = time uhi ulo
      s = time shi slo
  return $ ProcessResourceUsage{ userTime = u, systemTime = s }
 where
  get n = fmap fromIntegral $ peekElemOff p_sru n
  time hi lo = toRational hi + (lo % 1000000)

foreign import ccall unsafe "wait4"
   c_wait4 :: CPid -> Ptr CInt -> CInt -> Ptr CRusage -> IO CPid


-- The following was scarfed from
-- fptools/libraries/unix/System/Posix/Process.hsc?rev=1.4

waitOptions :: Bool -> Bool -> CInt
--             block   stopped
waitOptions False False = 1 -- (#const WNOHANG)
waitOptions False True  = 3 -- (#const (WNOHANG|WUNTRACED))
waitOptions True  False = 0
waitOptions True  True  = 2 -- (#const WUNTRACED)

-- Turn a (ptr to a) wait status into a ProcessStatus

decipherWaitStatus :: Ptr CInt -> IO ProcessStatus
decipherWaitStatus wstatp = do
  wstat <- peek wstatp
  if c_WIFEXITED wstat /= 0
      then do
        let exitstatus = c_WEXITSTATUS wstat
        if exitstatus == 0
           then return (Exited ExitSuccess)
           else return (Exited (ExitFailure (fromIntegral exitstatus)))
      else do
        if c_WIFSIGNALED wstat /= 0
           then do
                let termsig = c_WTERMSIG wstat
                return (Terminated (fromIntegral termsig))
           else do
                if c_WIFSTOPPED wstat /= 0
                   then do
                        let stopsig = c_WSTOPSIG wstat
                        return (Stopped (fromIntegral stopsig))
                   else do
                        ioError (mkIOError illegalOperationErrorType
                                   "waitStatus" Nothing Nothing)

{-

foreign import ccall unsafe "__hsunix_wifexited"
  c_WIFEXITED :: CInt -> CInt 

foreign import ccall unsafe "__hsunix_wexitstatus"
  c_WEXITSTATUS :: CInt -> CInt

foreign import ccall unsafe "__hsunix_wifsignaled"
  c_WIFSIGNALED :: CInt -> CInt

foreign import ccall unsafe "__hsunix_wtermsig"
  c_WTERMSIG :: CInt -> CInt 

foreign import ccall unsafe "__hsunix_wifstopped"
  c_WIFSTOPPED :: CInt -> CInt

foreign import ccall unsafe "__hsunix_wstopsig"
  c_WSTOPSIG :: CInt -> CInt

-}

c_WIFEXITED :: CInt -> CInt 
c_WIFEXITED c = h2c $ fromEnum $ low7 (c2h c) == 0

c_WEXITSTATUS :: CInt -> CInt
c_WEXITSTATUS c = h2c $ high8 $ c2h c

c_WIFSIGNALED :: CInt -> CInt
c_WIFSIGNALED c = h2c $ fromEnum $ c_WIFSTOPPED c /= 0 && c_WIFEXITED c /= 0

c_WTERMSIG :: CInt -> CInt 
c_WTERMSIG c = h2c $ low7 $ c2h c

c_WIFSTOPPED :: CInt -> CInt
c_WIFSTOPPED c = h2c $ fromEnum $ low8 (c2h c) == 127

c_WSTOPSIG :: CInt -> CInt
c_WSTOPSIG c = h2c $ high8 $ c2h c

low7  x = x `rem` 128
low8  x = x `rem` 256
high8 x = x `div` 256

c2h = fromIntegral
h2c = fromIntegral