Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Reading from /proc. Issues with strictness and laziness. Process statistics

I have really strange behaviour while reading files from /proc If I read /proc/pid/stat lazily with prelude's readFile - it works but not the way I want. Switching to strict reading with Data.ByteString.readFile gives me an empty string.

I need strict reading here to be able to compare the results of two reads within short interval.

So using System.IO.readFile to read /proc/pid/stat simply does not work. It gives me the same result within 0.5 sec interval. I figure this is due to laziness and half closed handle or something ... Opening and closing the file handle explicitly works.

h <- openFile "/proc/pid/stat" ReadMode
st1 <- hGetLine h; hClose h

But why do the above if we have the bytestring strict reading. Right?

This is where I got stuck.

import qualified Data.ByteString as B
B.readFile "/proc/pid/stat" >>= print

This always returns an empty string. Also tested in GHCI. Any suggestions. Thanks.

--- UPDATE ---

Thank you Daniel for suggestions.

This is what I actually need to do. This might help to show my dilemma in full and bring more general suggestions.

I need to calculate process statistics. Here is part of the code (just the CPU usage) as an example.

cpuUsage pid = do
  st1 <- readProc $ "/proc" </> pid </> "stat"
  threadDelay 500000 -- 0.5 sec
  st2 <- readProc $ "/proc" </> pid </> "stat"
  let sum1 = (read $ words st1 !! 13) +
             (read $ words st1 !! 14)
      sum2 = (read $ words st2 !! 13) +
             (read $ words st2 !! 14)
  return $ round $ fromIntegral (sum2 - sum1) * jiffy / delay * 100
  where
    jiffy = 0.01
    delay = 0.5
    readProc f = do
      h <- openFile f ReadMode
      c <- hGetLine h
      hClose h
      return c
  1. Prelude.readFile does not work due to the laziness
  2. Strict functions from ByteString don't work. Thank you Daniel for the explanation.
  3. withFile would work (it closes the handle properly) if I stuffed the whole computation in it but then the interval will not be strictly 0.5 as computations take time.
  4. Opening and closing handles explicitly and using hGetContents does not work! For the same reason readFile doesn't.

The only thing that work in this situation is explicitly opening and closing handles with hGetLine in above code snippet. But this is not good enough as some proc files are more then one line like /proc/meminfo.

So I need a function that would read the whole file strictly. Something like hGetContents but strict.

I was trying to do this:

readProc f = do
  h <- openFile f ReadMode
  c <- hGetContents h
  let c' = lines c
  hClose h
  return c'

Hoping that lines would trigger it to read the file in full. No luck. Still get an empty list.

Any help, suggestion is very appreciated.

like image 520
r.sendecky Avatar asked Apr 17 '12 02:04

r.sendecky


2 Answers

The ByteString code is

readFile :: FilePath -> IO ByteString
readFile f = bracket (openBinaryFile f ReadMode) hClose
    (\h -> hFileSize h >>= hGet h . fromIntegral)

But /proc/whatever isn't a real file, it's generated on demand, when you stat them to get the file size, you get 0. So ByteString's readFile successfully reads 0 bytes.

like image 103
Daniel Fischer Avatar answered Oct 13 '22 07:10

Daniel Fischer


Before coding this type of thing, it's usually a good idea to check if something already exists on Hackage. In this case, I found the procstat package, which seems to work nicely:

import System.Linux.ProcStat

cpuUsage pid = do
  Just before <- fmap procTotalTime <$> procStat pid
  threadDelay 500000 -- 0.5 sec
  Just after  <- fmap procTotalTime <$> procStat pid
  return . round $ fromIntegral (after - before) * jiffy / delay * 100
  where
    procTotalTime info = procUTime info + procSTime info
    jiffy = 0.01
    delay = 0.5
like image 24
hammar Avatar answered Oct 13 '22 06:10

hammar