packages feed

linux-perf-0.3: ghc-events-perf/ghc-events-perf.hs

-----------------------------------------------------------------------------
-- |
-- Copyright   : (c) 2012 Duncan Coutts, Bernie Pope, Mikolaj Konarski
-- License     : BSD-style
-- Maintainer  : florbitous@gmail.com
-- Stability   : experimental
-- Portability : ghc
--
-- A tool to help profiling a Haskell program using Linux 'perf',
-- ghc-events and, eventually, Threadscope.
--
-- The ghc-events-perf tool has a couple of commands
-- for obtaining 'perf' performance data for a Haskell program,
-- translating it to the ghc-events format, synchronizing and merging
-- with the standard ghc-events eventlog for the Haskell program,
-- and so making the data ready for display in Threadscope.
--
-- Usage:
--
-- > ghc-events-perf command command-args
--
-- Getting help:
--
-- > ghc-events-perf help
-- > ghc-events-perf help command
-----------------------------------------------------------------------------

module Main where

import System.Posix.Process
import System.Environment
import System.Exit
import System.FilePath
import System.Process
import System.IO (hPutStrLn, stderr)
import Control.Monad

main :: IO ()
main = getArgs >>= command

command :: [String] -> IO ()
command [helpString] | helpString `elem` ["-h", "--help", "help"] =
  putStrLn usage

command ["help", "help"] =
  putStrLn "Helps."

command ["help", "record"] = do
  let args = ["+GhcEventsPerf", "-h"]
  executeFile "ghc-events-perf-record" True args Nothing

command ["help", "convert"] =
  putStr $ unlines $
    [ "Usage: ghc-events-perf convert program-name [out-file perf-file eventlog-file]"
    , "  program-name   the name of the profiled Haskell program"
    , "  out-file       where to save the resulting compound eventlog"
    , "  perf-file      path to the perf data file for the Haskell program"
    , "  eventlog-file  path to the GHC RTS eventlog of the Haskell program"
    ]

command ("record" : args) = do
  myPath <- getExecutablePath
  let commonPath = fst $ splitFileName myPath
      -- We assume ghc-events-perf-record is in the same directory
      -- as ghc-events-perf. Needed for 'sudo', because root needn't
      -- have user's ~/.cabal/bin in his PATH.
      recordPath = combine commonPath "ghc-events-perf-record"
      recordArgs = "--RTS" : args
  executeFile recordPath True recordArgs Nothing

command ["convert", program_name, out_file, perf_file, eventlog_file] = do
  let sync_eventlog_file = program_name ++ ".perf.eventlog"
      syncArgs = [ program_name
                 , perf_file
                 , sync_eventlog_file
                 ]
  putStrLn "Translating and synchronizing..."
  syncHandle <- runProcess "ghc-events-perf-sync" syncArgs
                           Nothing Nothing Nothing Nothing Nothing
  void $ waitForProcess syncHandle
  let mergeArgs = [ "merge"
                  , out_file
                  , eventlog_file
                  , sync_eventlog_file
                  ]
  putStrLn "Merging with the standard eventlog..."
  executeFile "ghc-events" True mergeArgs Nothing

command ["convert", program_name] = do
  let out_file = program_name ++ ".total.eventlog"
      perf_file = "data.perf"  -- the same as defaultPerfOutputFile in record
      eventlog_file = program_name ++ ".eventlog"
  command ["convert", program_name, out_file, perf_file, eventlog_file]

command _ = putStrLn usage >> die "Unrecognized command"

usage :: String
usage = "Usage: ghc-events-perf command command-args\n\nThe available commands are: record convert help.\n\nSee 'ghc-events-perf help command' for more information on a specific command."

-- Exit the program with an error message.
die :: String -> IO a
die s = hPutStrLn stderr s >> exitWith (ExitFailure 1)