    -- | @futhark profile@
    module Futhark.CLI.Profile (main) where

    import Control.Exception (catch)
    import Data.ByteString.Lazy.Char8 qualified as BS
    import Data.List qualified as L
    import Data.Map qualified as M
    import Data.Text qualified as T
    import Data.Text.IO qualified as T
    import Futhark.Bench
    import Futhark.Util (showText)
    import Futhark.Util.Options
    import System.Directory (createDirectoryIfMissing, removePathForcibly)
    import System.Exit
    import System.FilePath
    import System.IO
    import Text.Printf

    commonPrefix :: (Eq e) => [e] -> [e] -> [e]
    commonPrefix :: forall e. Eq e => [e] -> [e] -> [e]
    commonPrefix [e]
    _ [] = []
    commonPrefix [] [e]
    _ = []
    commonPrefix (e
    x : [e]
    xs) (e
    y : [e]
    ys)
      | e
    x e -> e -> Bool
    forall a. Eq a => a -> a -> Bool
    == e
    y = e
    x e -> [e] -> [e]
    forall a. a -> [a] -> [a]
    : [e] -> [e] -> [e]
    forall e. Eq e => [e] -> [e] -> [e]
    commonPrefix [e]
    xs [e]
    ys
      | Bool
    otherwise = []

    longestCommonPrefix :: [FilePath] -> FilePath
    longestCommonPrefix :: [String] -> String
    longestCommonPrefix [] = String
    ""
    longestCommonPrefix (String
    x : [String]
    xs) = (String -> String -> String) -> String -> [String] -> String
    forall a b. (a -> b -> b) -> b -> [a] -> b
    forall (t :: * -> *) a b.
    Foldable t =>
    (a -> b -> b) -> b -> t a -> b
    foldr String -> String -> String
    forall e. Eq e => [e] -> [e] -> [e]
    commonPrefix String
    x [String]
    xs

    memoryReport :: M.Map T.Text Integer -> T.Text
    memoryReport :: Map Text Integer -> Text
    memoryReport = [Text] -> Text
    T.unlines ([Text] -> Text)
    -> (Map Text Integer -> [Text]) -> Map Text Integer -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text
    "Peak memory usage in bytes" :) ([Text] -> [Text])
    -> (Map Text Integer -> [Text]) -> Map Text Integer -> [Text]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ((Text, Integer) -> Text) -> [(Text, Integer)] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Text, Integer) -> Text
    forall {a}. Show a => (Text, a) -> Text
    f ([(Text, Integer)] -> [Text])
    -> (Map Text Integer -> [(Text, Integer)])
    -> Map Text Integer
    -> [Text]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Map Text Integer -> [(Text, Integer)]
    forall k a. Map k a -> [(k, a)]
    M.toList
      where
        f :: (Text, a) -> Text
    f (Text
    space, a
    bytes) = Text
    space Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ": " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> a -> Text
    forall a. Show a => a -> Text
    showText a
    bytes

    padRight :: Int -> T.Text -> T.Text
    padRight :: Int -> Text -> Text
    padRight Int
    k Text
    s = Text
    s Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Int -> Text -> Text
    T.replicate (Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Text -> Int
    T.length Text
    s) Text
    " "

    padLeft :: Int -> T.Text -> T.Text
    padLeft :: Int -> Text -> Text
    padLeft Int
    k Text
    s = Int -> Text -> Text
    T.replicate (Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Text -> Int
    T.length Text
    s) Text
    " " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    s

    data EvSummary = EvSummary
      { EvSummary -> Integer
    evCount :: Integer,
        EvSummary -> Double
    evSum :: Double,
        EvSummary -> Double
    evMin :: Double,
        EvSummary -> Double
    evMax :: Double
      }

    tabulateEvents :: [ProfilingEvent] -> T.Text
    tabulateEvents :: [ProfilingEvent] -> Text
    tabulateEvents = [(Text, EvSummary)] -> Text
    mkRows ([(Text, EvSummary)] -> Text)
    -> ([ProfilingEvent] -> [(Text, EvSummary)])
    -> [ProfilingEvent]
    -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Map Text EvSummary -> [(Text, EvSummary)]
    forall k a. Map k a -> [(k, a)]
    M.toList (Map Text EvSummary -> [(Text, EvSummary)])
    -> ([ProfilingEvent] -> Map Text EvSummary)
    -> [ProfilingEvent]
    -> [(Text, EvSummary)]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (EvSummary -> EvSummary -> EvSummary)
    -> [(Text, EvSummary)] -> Map Text EvSummary
    forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
    M.fromListWith EvSummary -> EvSummary -> EvSummary
    comb ([(Text, EvSummary)] -> Map Text EvSummary)
    -> ([ProfilingEvent] -> [(Text, EvSummary)])
    -> [ProfilingEvent]
    -> Map Text EvSummary
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ProfilingEvent -> (Text, EvSummary))
    -> [ProfilingEvent] -> [(Text, EvSummary)]
    forall a b. (a -> b) -> [a] -> [b]
    map ProfilingEvent -> (Text, EvSummary)
    pair
      where
        pair :: ProfilingEvent -> (Text, EvSummary)
    pair (ProfilingEvent Text
    name Double
    dur Text
    _ Value
    _details) = (Text
    name, Integer -> Double -> Double -> Double -> EvSummary
    EvSummary Integer
    1 Double
    dur Double
    dur Double
    dur)
        comb :: EvSummary -> EvSummary -> EvSummary
    comb (EvSummary Integer
    xn Double
    xdur Double
    xmin Double
    xmax) (EvSummary Integer
    yn Double
    ydur Double
    ymin Double
    ymax) =
          Integer -> Double -> Double -> Double -> EvSummary
    EvSummary (Integer
    xn Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    + Integer
    yn) (Double
    xdur Double -> Double -> Double
    forall a. Num a => a -> a -> a
    + Double
    ydur) (Double -> Double -> Double
    forall a. Ord a => a -> a -> a
    min Double
    xmin Double
    ymin) (Double -> Double -> Double
    forall a. Ord a => a -> a -> a
    max Double
    xmax Double
    ymax)
        numpad :: Int
    numpad = Int
    15
        mkRows :: [(Text, EvSummary)] -> Text
    mkRows [(Text, EvSummary)]
    rows =
          let longest :: Int
    longest = (Int -> Int -> Int) -> Int -> [Int] -> Int
    forall b a. (b -> a -> b) -> b -> [a] -> b
    forall (t :: * -> *) b a.
    Foldable t =>
    (b -> a -> b) -> b -> t a -> b
    foldl Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max Int
    numpad ([Int] -> Int) -> [Int] -> Int
    forall a b. (a -> b) -> a -> b
    $ ((Text, EvSummary) -> Int) -> [(Text, EvSummary)] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map (Text -> Int
    T.length (Text -> Int)
    -> ((Text, EvSummary) -> Text) -> (Text, EvSummary) -> Int
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text, EvSummary) -> Text
    forall a b. (a, b) -> a
    fst) [(Text, EvSummary)]
    rows
              total :: Double
    total = [Double] -> Double
    forall a. Num a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
    sum ([Double] -> Double) -> [Double] -> Double
    forall a b. (a -> b) -> a -> b
    $ ((Text, EvSummary) -> Double) -> [(Text, EvSummary)] -> [Double]
    forall a b. (a -> b) -> [a] -> [b]
    map (EvSummary -> Double
    evSum (EvSummary -> Double)
    -> ((Text, EvSummary) -> EvSummary) -> (Text, EvSummary) -> Double
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text, EvSummary) -> EvSummary
    forall a b. (a, b) -> b
    snd) [(Text, EvSummary)]
    rows
              header :: Text
    header = Int -> Text
    headerRow Int
    longest
              splitter :: Text
    splitter = (Char -> Char) -> Text -> Text
    T.map (Char -> Char -> Char
    forall a b. a -> b -> a
    const Char
    '-') Text
    header
              bottom :: Text
    bottom =
                [Text] -> Text
    T.unwords
                  [ Integer -> Text
    forall a. Show a => a -> Text
    showText ([Integer] -> Integer
    forall a. Num a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
    sum (((Text, EvSummary) -> Integer) -> [(Text, EvSummary)] -> [Integer]
    forall a b. (a -> b) -> [a] -> [b]
    map (EvSummary -> Integer
    evCount (EvSummary -> Integer)
    -> ((Text, EvSummary) -> EvSummary) -> (Text, EvSummary) -> Integer
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text, EvSummary) -> EvSummary
    forall a b. (a, b) -> b
    snd) [(Text, EvSummary)]
    rows)),
                    Text
    "events with a total runtime of",
                    String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.2fμs" Double
    total
                  ]
           in [Text] -> Text
    T.unlines ([Text] -> Text) -> [Text] -> Text
    forall a b. (a -> b) -> a -> b
    $
                Text
    header
                  Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : Text
    splitter
                  Text -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    : ((Text, EvSummary) -> Text) -> [(Text, EvSummary)] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Int -> Double -> (Text, EvSummary) -> Text
    mkRow Int
    longest Double
    total) [(Text, EvSummary)]
    rows
                    [Text] -> [Text] -> [Text]
    forall a. Semigroup a => a -> a -> a
    <> [Text
    splitter, Text
    bottom]
        headerRow :: Int -> Text
    headerRow Int
    longest =
          [Text] -> Text
    T.unwords
            [ Int -> Text -> Text
    padLeft Int
    longest Text
    "Cost centre",
              Int -> Text -> Text
    padLeft Int
    numpad Text
    "count",
              Int -> Text -> Text
    padLeft Int
    numpad Text
    "sum",
              Int -> Text -> Text
    padLeft Int
    numpad Text
    "avg",
              Int -> Text -> Text
    padLeft Int
    numpad Text
    "min",
              Int -> Text -> Text
    padLeft Int
    numpad Text
    "max",
              Int -> Text -> Text
    padLeft Int
    numpad Text
    "fraction"
            ]
        mkRow :: Int -> Double -> (Text, EvSummary) -> Text
    mkRow Int
    longest Double
    total (Text
    name, EvSummary
    ev) =
          [Text] -> Text
    T.unwords
            [ Int -> Text -> Text
    padRight Int
    longest Text
    name,
              Int -> Text -> Text
    padLeft Int
    numpad (Integer -> Text
    forall a. Show a => a -> Text
    showText (EvSummary -> Integer
    evCount EvSummary
    ev)),
              Int -> Text -> Text
    padLeft Int
    numpad (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.2fμs" (EvSummary -> Double
    evSum EvSummary
    ev),
              Int -> Text -> Text
    padLeft Int
    numpad (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.2fμs" (Double -> String) -> Double -> String
    forall a b. (a -> b) -> a -> b
    $ EvSummary -> Double
    evSum EvSummary
    ev Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Integer -> Double
    forall a. Num a => Integer -> a
    fromInteger (EvSummary -> Integer
    evCount EvSummary
    ev),
              Int -> Text -> Text
    padLeft Int
    numpad (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.2fμs" (EvSummary -> Double
    evMin EvSummary
    ev),
              Int -> Text -> Text
    padLeft Int
    numpad (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.2fμs" (EvSummary -> Double
    evMax EvSummary
    ev),
              Int -> Text -> Text
    padLeft Int
    numpad (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ String -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.4f" (EvSummary -> Double
    evSum EvSummary
    ev Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    total)
            ]

    timeline :: [ProfilingEvent] -> T.Text
    timeline :: [ProfilingEvent] -> Text
    timeline = [Text] -> Text
    T.unlines ([Text] -> Text)
    -> ([ProfilingEvent] -> [Text]) -> [ProfilingEvent] -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Text] -> [[Text]] -> [Text]
    forall a. [a] -> [[a]] -> [a]
    L.intercalate [Text
    ""] ([[Text]] -> [Text])
    -> ([ProfilingEvent] -> [[Text]]) -> [ProfilingEvent] -> [Text]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (ProfilingEvent -> [Text]) -> [ProfilingEvent] -> [[Text]]
    forall a b. (a -> b) -> [a] -> [b]
    map ProfilingEvent -> [Text]
    onEvent
      where
        onEvent :: ProfilingEvent -> [Text]
    onEvent (ProfilingEvent Text
    name Double
    duration Text
    provenance Value
    _details) =
          [ Text
    name,
            Text
    "Duration: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Double -> Text
    forall a. Show a => a -> Text
    showText Double
    duration Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    " μs",
            Text
    "At: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    provenance
          ]

    data TargetFiles = TargetFiles
      { TargetFiles -> String
    summaryFile :: FilePath,
        TargetFiles -> String
    timelineFile :: FilePath
      }

    writeAnalysis :: TargetFiles -> ProfilingReport -> IO ()
    writeAnalysis :: TargetFiles -> ProfilingReport -> IO ()
    writeAnalysis TargetFiles
    tf ProfilingReport
    r = do
      String -> Text -> IO ()
    T.writeFile (TargetFiles -> String
    summaryFile TargetFiles
    tf) (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $
        Map Text Integer -> Text
    memoryReport (ProfilingReport -> Map Text Integer
    profilingMemory ProfilingReport
    r)
          Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "\n\n"
          Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> [ProfilingEvent] -> Text
    tabulateEvents (ProfilingReport -> [ProfilingEvent]
    profilingEvents ProfilingReport
    r)
      String -> Text -> IO ()
    T.writeFile (TargetFiles -> String
    timelineFile TargetFiles
    tf) (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $
        [ProfilingEvent] -> Text
    timeline (ProfilingReport -> [ProfilingEvent]
    profilingEvents ProfilingReport
    r)

    prepareDir :: FilePath -> IO FilePath
    prepareDir :: String -> IO String
    prepareDir String
    json_path = do
      let top_dir :: String
    top_dir = String -> String
    takeFileName String
    json_path String -> String -> String
    -<.> String
    "prof"
      Handle -> Text -> IO ()
    T.hPutStrLn Handle
    stderr (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Text
    "Writing results to " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> String -> Text
    T.pack String
    top_dir Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "/"
      String -> IO ()
    removePathForcibly String
    top_dir
      String -> IO String
    forall a. a -> IO a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure String
    top_dir

    analyseProfilingReport :: FilePath -> ProfilingReport -> IO ()
    analyseProfilingReport :: String -> ProfilingReport -> IO ()
    analyseProfilingReport String
    json_path ProfilingReport
    r = do
      String
    top_dir <- String -> IO String
    prepareDir String
    json_path
      Bool -> String -> IO ()
    createDirectoryIfMissing Bool
    True String
    top_dir
      let tf :: TargetFiles
    tf =
            TargetFiles
              { summaryFile :: String
    summaryFile = String
    top_dir String -> String -> String
    </> String
    "summary",
                timelineFile :: String
    timelineFile = String
    top_dir String -> String -> String
    </> String
    "timeline"
              }
      TargetFiles -> ProfilingReport -> IO ()
    writeAnalysis TargetFiles
    tf ProfilingReport
    r

    analyseBenchResults :: FilePath -> [BenchResult] -> IO ()
    analyseBenchResults :: String -> [BenchResult] -> IO ()
    analyseBenchResults String
    json_path [BenchResult]
    bench_results = do
      String
    top_dir <- String -> IO String
    prepareDir String
    json_path
      Handle -> Text -> IO ()
    T.hPutStrLn Handle
    stderr (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Text
    "Stripping '" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> String -> Text
    T.pack String
    prefix Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    "' from program paths."
      (BenchResult -> IO ()) -> [BenchResult] -> IO ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    (a -> m b) -> t a -> m ()
    mapM_ (String -> BenchResult -> IO ()
    onBenchResult String
    top_dir) [BenchResult]
    bench_results
      where
        prefix :: String
    prefix = [String] -> String
    longestCommonPrefix ([String] -> String) -> [String] -> String
    forall a b. (a -> b) -> a -> b
    $ (BenchResult -> String) -> [BenchResult] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map BenchResult -> String
    benchResultProg [BenchResult]
    bench_results

        -- Eliminate characters that are filesystem-meaningful.
        escape :: Char -> Char
    escape Char
    '/' = Char
    '_'
        escape Char
    c = Char
    c

        problem :: Text -> Text -> Text -> IO ()
    problem Text
    prog_name Text
    name Text
    what =
          Handle -> Text -> IO ()
    T.hPutStrLn Handle
    stderr (Text -> IO ()) -> Text -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Text
    prog_name Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    " dataset " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    name Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ": " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    what

        onBenchResult :: String -> BenchResult -> IO ()
    onBenchResult String
    top_dir (BenchResult String
    prog_path [DataResult]
    data_results) = do
          let (String
    prog_path', String
    entry) = (Char -> Bool) -> String -> (String, String)
    forall a. (a -> Bool) -> [a] -> ([a], [a])
    span (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    ':') String
    prog_path
              prog_name :: String
    prog_name = Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop (String -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length String
    prefix) String
    prog_path'
              prog_dir :: String
    prog_dir = String
    top_dir String -> String -> String
    </> String -> String
    dropExtension String
    prog_name String -> String -> String
    </> Int -> String -> String
    forall a. Int -> [a] -> [a]
    drop Int
    1 String
    entry
          Bool -> String -> IO ()
    createDirectoryIfMissing Bool
    True String
    prog_dir
          (DataResult -> IO ()) -> [DataResult] -> IO ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    (a -> m b) -> t a -> m ()
    mapM_ (String -> Text -> DataResult -> IO ()
    onDataResult String
    prog_dir (String -> Text
    T.pack String
    prog_name)) [DataResult]
    data_results

        onDataResult :: String -> Text -> DataResult -> IO ()
    onDataResult String
    _ Text
    prog_name (DataResult Text
    name (Left Text
    _)) =
          Text -> Text -> Text -> IO ()
    problem Text
    prog_name Text
    name Text
    "execution failed"
        onDataResult String
    prog_dir Text
    prog_name (DataResult Text
    name (Right Result
    res)) = do
          let name' :: String
    name' = String
    prog_dir String -> String -> String
    </> Text -> String
    T.unpack ((Char -> Char) -> Text -> Text
    T.map Char -> Char
    escape Text
    name)
          case Result -> Maybe Text
    stdErr Result
    res of
            Maybe Text
    Nothing -> Text -> Text -> Text -> IO ()
    problem Text
    prog_name Text
    name Text
    "no log recorded"
            Just Text
    text -> String -> Text -> IO ()
    T.writeFile (String
    name' String -> String -> String
    <.> String
    ".log") Text
    text
          case Result -> Maybe ProfilingReport
    report Result
    res of
            Maybe ProfilingReport
    Nothing -> Text -> Text -> Text -> IO ()
    problem Text
    prog_name Text
    name Text
    "no profiling information"
            Just ProfilingReport
    r ->
              let tf :: TargetFiles
    tf =
                    TargetFiles
                      { summaryFile :: String
    summaryFile = String
    name' String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    ".summary",
                        timelineFile :: String
    timelineFile = String
    name' String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    ".timeline"
                      }
               in TargetFiles -> ProfilingReport -> IO ()
    writeAnalysis TargetFiles
    tf ProfilingReport
    r

    readFileSafely :: FilePath -> IO (Either String BS.ByteString)
    readFileSafely :: String -> IO (Either String ByteString)
    readFileSafely String
    filepath =
      (ByteString -> Either String ByteString
    forall a b. b -> Either a b
    Right (ByteString -> Either String ByteString)
    -> IO ByteString -> IO (Either String ByteString)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> String -> IO ByteString
    BS.readFile String
    filepath) IO (Either String ByteString)
    -> (IOError -> IO (Either String ByteString))
    -> IO (Either String ByteString)
    forall e a. Exception e => IO a -> (e -> IO a) -> IO a
    `catch` IOError -> IO (Either String ByteString)
    forall {f :: * -> *} {b}.
    Applicative f =>
    IOError -> f (Either String b)
    couldNotRead
      where
        couldNotRead :: IOError -> f (Either String b)
    couldNotRead IOError
    e = Either String b -> f (Either String b)
    forall a. a -> f a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (Either String b -> f (Either String b))
    -> Either String b -> f (Either String b)
    forall a b. (a -> b) -> a -> b
    $ String -> Either String b
    forall a b. a -> Either a b
    Left (String -> Either String b) -> String -> Either String b
    forall a b. (a -> b) -> a -> b
    $ IOError -> String
    forall a. Show a => a -> String
    show (IOError
    e :: IOError)

    onFile :: FilePath -> IO ()
    onFile :: String -> IO ()
    onFile String
    json_path = do
      Either String ByteString
    s <- String -> IO (Either String ByteString)
    readFileSafely String
    json_path
      case Either String ByteString
    s of
        Left String
    a -> do
          Handle -> String -> IO ()
    hPutStrLn Handle
    stderr String
    a
          ExitCode -> IO ()
    forall a. ExitCode -> IO a
    exitWith (ExitCode -> IO ()) -> ExitCode -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> ExitCode
    ExitFailure Int
    2
        Right ByteString
    s' ->
          case ByteString -> Either String [BenchResult]
    decodeBenchResults ByteString
    s' of
            Left String
    _ ->
              case ByteString -> Maybe ProfilingReport
    decodeProfilingReport ByteString
    s' of
                Maybe ProfilingReport
    Nothing -> do
                  Handle -> String -> IO ()
    hPutStrLn Handle
    stderr (String -> IO ()) -> String -> IO ()
    forall a b. (a -> b) -> a -> b
    $
                    String
    "Cannot recognise " String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    json_path String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    " as benchmark results or a profiling report."
                Just ProfilingReport
    pr ->
                  String -> ProfilingReport -> IO ()
    analyseProfilingReport String
    json_path ProfilingReport
    pr
            Right [BenchResult]
    br -> String -> [BenchResult] -> IO ()
    analyseBenchResults String
    json_path [BenchResult]
    br

    -- | Run @futhark profile@.
    main :: String -> [String] -> IO ()
    main :: String -> [String] -> IO ()
    main = ()
    -> [FunOptDescr ()]
    -> String
    -> ([String] -> () -> Maybe (IO ()))
    -> String
    -> [String]
    -> IO ()
    forall cfg.
    cfg
    -> [FunOptDescr cfg]
    -> String
    -> ([String] -> cfg -> Maybe (IO ()))
    -> String
    -> [String]
    -> IO ()
    mainWithOptions () [] String
    "[files]" [String] -> () -> Maybe (IO ())
    forall {t :: * -> *}. Foldable t => t String -> () -> Maybe (IO ())
    f
      where
        f :: t String -> () -> Maybe (IO ())
    f t String
    files () = IO () -> Maybe (IO ())
    forall a. a -> Maybe a
    Just (IO () -> Maybe (IO ())) -> IO () -> Maybe (IO ())
    forall a b. (a -> b) -> a -> b
    $ (String -> IO ()) -> t String -> IO ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    (a -> m b) -> t a -> m ()
    mapM_ String -> IO ()
    onFile t String
    files