cdp-0.0.1.0: src/CDP/Domains/Profiler.hs
{-# LANGUAGE OverloadedStrings, RecordWildCards, TupleSections #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeFamilies #-}
{- |
= Profiler
-}
module CDP.Domains.Profiler (module CDP.Domains.Profiler) where
import Control.Applicative ((<$>))
import Control.Monad
import Control.Monad.Loops
import Control.Monad.Trans (liftIO)
import qualified Data.Map as M
import Data.Maybe
import Data.Functor.Identity
import Data.String
import qualified Data.Text as T
import qualified Data.List as List
import qualified Data.Text.IO as TI
import qualified Data.Vector as V
import Data.Aeson.Types (Parser(..))
import Data.Aeson (FromJSON (..), ToJSON (..), (.:), (.:?), (.=), (.!=), (.:!))
import qualified Data.Aeson as A
import qualified Network.HTTP.Simple as Http
import qualified Network.URI as Uri
import qualified Network.WebSockets as WS
import Control.Concurrent
import qualified Data.ByteString.Lazy as BS
import qualified Data.Map as Map
import Data.Proxy
import System.Random
import GHC.Generics
import Data.Char
import Data.Default
import CDP.Internal.Utils
import CDP.Domains.Debugger as Debugger
import CDP.Domains.Runtime as Runtime
-- | Type 'Profiler.ProfileNode'.
-- Profile node. Holds callsite information, execution statistics and child nodes.
data ProfilerProfileNode = ProfilerProfileNode
{
-- | Unique id of the node.
profilerProfileNodeId :: Int,
-- | Function location.
profilerProfileNodeCallFrame :: Runtime.RuntimeCallFrame,
-- | Number of samples where this node was on top of the call stack.
profilerProfileNodeHitCount :: Maybe Int,
-- | Child node ids.
profilerProfileNodeChildren :: Maybe [Int],
-- | The reason of being not optimized. The function may be deoptimized or marked as don't
-- optimize.
profilerProfileNodeDeoptReason :: Maybe T.Text,
-- | An array of source position ticks.
profilerProfileNodePositionTicks :: Maybe [ProfilerPositionTickInfo]
}
deriving (Eq, Show)
instance FromJSON ProfilerProfileNode where
parseJSON = A.withObject "ProfilerProfileNode" $ \o -> ProfilerProfileNode
<$> o A..: "id"
<*> o A..: "callFrame"
<*> o A..:? "hitCount"
<*> o A..:? "children"
<*> o A..:? "deoptReason"
<*> o A..:? "positionTicks"
instance ToJSON ProfilerProfileNode where
toJSON p = A.object $ catMaybes [
("id" A..=) <$> Just (profilerProfileNodeId p),
("callFrame" A..=) <$> Just (profilerProfileNodeCallFrame p),
("hitCount" A..=) <$> (profilerProfileNodeHitCount p),
("children" A..=) <$> (profilerProfileNodeChildren p),
("deoptReason" A..=) <$> (profilerProfileNodeDeoptReason p),
("positionTicks" A..=) <$> (profilerProfileNodePositionTicks p)
]
-- | Type 'Profiler.Profile'.
-- Profile.
data ProfilerProfile = ProfilerProfile
{
-- | The list of profile nodes. First item is the root node.
profilerProfileNodes :: [ProfilerProfileNode],
-- | Profiling start timestamp in microseconds.
profilerProfileStartTime :: Double,
-- | Profiling end timestamp in microseconds.
profilerProfileEndTime :: Double,
-- | Ids of samples top nodes.
profilerProfileSamples :: Maybe [Int],
-- | Time intervals between adjacent samples in microseconds. The first delta is relative to the
-- profile startTime.
profilerProfileTimeDeltas :: Maybe [Int]
}
deriving (Eq, Show)
instance FromJSON ProfilerProfile where
parseJSON = A.withObject "ProfilerProfile" $ \o -> ProfilerProfile
<$> o A..: "nodes"
<*> o A..: "startTime"
<*> o A..: "endTime"
<*> o A..:? "samples"
<*> o A..:? "timeDeltas"
instance ToJSON ProfilerProfile where
toJSON p = A.object $ catMaybes [
("nodes" A..=) <$> Just (profilerProfileNodes p),
("startTime" A..=) <$> Just (profilerProfileStartTime p),
("endTime" A..=) <$> Just (profilerProfileEndTime p),
("samples" A..=) <$> (profilerProfileSamples p),
("timeDeltas" A..=) <$> (profilerProfileTimeDeltas p)
]
-- | Type 'Profiler.PositionTickInfo'.
-- Specifies a number of samples attributed to a certain source position.
data ProfilerPositionTickInfo = ProfilerPositionTickInfo
{
-- | Source line number (1-based).
profilerPositionTickInfoLine :: Int,
-- | Number of samples attributed to the source line.
profilerPositionTickInfoTicks :: Int
}
deriving (Eq, Show)
instance FromJSON ProfilerPositionTickInfo where
parseJSON = A.withObject "ProfilerPositionTickInfo" $ \o -> ProfilerPositionTickInfo
<$> o A..: "line"
<*> o A..: "ticks"
instance ToJSON ProfilerPositionTickInfo where
toJSON p = A.object $ catMaybes [
("line" A..=) <$> Just (profilerPositionTickInfoLine p),
("ticks" A..=) <$> Just (profilerPositionTickInfoTicks p)
]
-- | Type 'Profiler.CoverageRange'.
-- Coverage data for a source range.
data ProfilerCoverageRange = ProfilerCoverageRange
{
-- | JavaScript script source offset for the range start.
profilerCoverageRangeStartOffset :: Int,
-- | JavaScript script source offset for the range end.
profilerCoverageRangeEndOffset :: Int,
-- | Collected execution count of the source range.
profilerCoverageRangeCount :: Int
}
deriving (Eq, Show)
instance FromJSON ProfilerCoverageRange where
parseJSON = A.withObject "ProfilerCoverageRange" $ \o -> ProfilerCoverageRange
<$> o A..: "startOffset"
<*> o A..: "endOffset"
<*> o A..: "count"
instance ToJSON ProfilerCoverageRange where
toJSON p = A.object $ catMaybes [
("startOffset" A..=) <$> Just (profilerCoverageRangeStartOffset p),
("endOffset" A..=) <$> Just (profilerCoverageRangeEndOffset p),
("count" A..=) <$> Just (profilerCoverageRangeCount p)
]
-- | Type 'Profiler.FunctionCoverage'.
-- Coverage data for a JavaScript function.
data ProfilerFunctionCoverage = ProfilerFunctionCoverage
{
-- | JavaScript function name.
profilerFunctionCoverageFunctionName :: T.Text,
-- | Source ranges inside the function with coverage data.
profilerFunctionCoverageRanges :: [ProfilerCoverageRange],
-- | Whether coverage data for this function has block granularity.
profilerFunctionCoverageIsBlockCoverage :: Bool
}
deriving (Eq, Show)
instance FromJSON ProfilerFunctionCoverage where
parseJSON = A.withObject "ProfilerFunctionCoverage" $ \o -> ProfilerFunctionCoverage
<$> o A..: "functionName"
<*> o A..: "ranges"
<*> o A..: "isBlockCoverage"
instance ToJSON ProfilerFunctionCoverage where
toJSON p = A.object $ catMaybes [
("functionName" A..=) <$> Just (profilerFunctionCoverageFunctionName p),
("ranges" A..=) <$> Just (profilerFunctionCoverageRanges p),
("isBlockCoverage" A..=) <$> Just (profilerFunctionCoverageIsBlockCoverage p)
]
-- | Type 'Profiler.ScriptCoverage'.
-- Coverage data for a JavaScript script.
data ProfilerScriptCoverage = ProfilerScriptCoverage
{
-- | JavaScript script id.
profilerScriptCoverageScriptId :: Runtime.RuntimeScriptId,
-- | JavaScript script name or url.
profilerScriptCoverageUrl :: T.Text,
-- | Functions contained in the script that has coverage data.
profilerScriptCoverageFunctions :: [ProfilerFunctionCoverage]
}
deriving (Eq, Show)
instance FromJSON ProfilerScriptCoverage where
parseJSON = A.withObject "ProfilerScriptCoverage" $ \o -> ProfilerScriptCoverage
<$> o A..: "scriptId"
<*> o A..: "url"
<*> o A..: "functions"
instance ToJSON ProfilerScriptCoverage where
toJSON p = A.object $ catMaybes [
("scriptId" A..=) <$> Just (profilerScriptCoverageScriptId p),
("url" A..=) <$> Just (profilerScriptCoverageUrl p),
("functions" A..=) <$> Just (profilerScriptCoverageFunctions p)
]
-- | Type of the 'Profiler.consoleProfileFinished' event.
data ProfilerConsoleProfileFinished = ProfilerConsoleProfileFinished
{
profilerConsoleProfileFinishedId :: T.Text,
-- | Location of console.profileEnd().
profilerConsoleProfileFinishedLocation :: Debugger.DebuggerLocation,
profilerConsoleProfileFinishedProfile :: ProfilerProfile,
-- | Profile title passed as an argument to console.profile().
profilerConsoleProfileFinishedTitle :: Maybe T.Text
}
deriving (Eq, Show)
instance FromJSON ProfilerConsoleProfileFinished where
parseJSON = A.withObject "ProfilerConsoleProfileFinished" $ \o -> ProfilerConsoleProfileFinished
<$> o A..: "id"
<*> o A..: "location"
<*> o A..: "profile"
<*> o A..:? "title"
instance Event ProfilerConsoleProfileFinished where
eventName _ = "Profiler.consoleProfileFinished"
-- | Type of the 'Profiler.consoleProfileStarted' event.
data ProfilerConsoleProfileStarted = ProfilerConsoleProfileStarted
{
profilerConsoleProfileStartedId :: T.Text,
-- | Location of console.profile().
profilerConsoleProfileStartedLocation :: Debugger.DebuggerLocation,
-- | Profile title passed as an argument to console.profile().
profilerConsoleProfileStartedTitle :: Maybe T.Text
}
deriving (Eq, Show)
instance FromJSON ProfilerConsoleProfileStarted where
parseJSON = A.withObject "ProfilerConsoleProfileStarted" $ \o -> ProfilerConsoleProfileStarted
<$> o A..: "id"
<*> o A..: "location"
<*> o A..:? "title"
instance Event ProfilerConsoleProfileStarted where
eventName _ = "Profiler.consoleProfileStarted"
-- | Type of the 'Profiler.preciseCoverageDeltaUpdate' event.
data ProfilerPreciseCoverageDeltaUpdate = ProfilerPreciseCoverageDeltaUpdate
{
-- | Monotonically increasing time (in seconds) when the coverage update was taken in the backend.
profilerPreciseCoverageDeltaUpdateTimestamp :: Double,
-- | Identifier for distinguishing coverage events.
profilerPreciseCoverageDeltaUpdateOccasion :: T.Text,
-- | Coverage data for the current isolate.
profilerPreciseCoverageDeltaUpdateResult :: [ProfilerScriptCoverage]
}
deriving (Eq, Show)
instance FromJSON ProfilerPreciseCoverageDeltaUpdate where
parseJSON = A.withObject "ProfilerPreciseCoverageDeltaUpdate" $ \o -> ProfilerPreciseCoverageDeltaUpdate
<$> o A..: "timestamp"
<*> o A..: "occasion"
<*> o A..: "result"
instance Event ProfilerPreciseCoverageDeltaUpdate where
eventName _ = "Profiler.preciseCoverageDeltaUpdate"
-- | Parameters of the 'Profiler.disable' command.
data PProfilerDisable = PProfilerDisable
deriving (Eq, Show)
pProfilerDisable
:: PProfilerDisable
pProfilerDisable
= PProfilerDisable
instance ToJSON PProfilerDisable where
toJSON _ = A.Null
instance Command PProfilerDisable where
type CommandResponse PProfilerDisable = ()
commandName _ = "Profiler.disable"
fromJSON = const . A.Success . const ()
-- | Parameters of the 'Profiler.enable' command.
data PProfilerEnable = PProfilerEnable
deriving (Eq, Show)
pProfilerEnable
:: PProfilerEnable
pProfilerEnable
= PProfilerEnable
instance ToJSON PProfilerEnable where
toJSON _ = A.Null
instance Command PProfilerEnable where
type CommandResponse PProfilerEnable = ()
commandName _ = "Profiler.enable"
fromJSON = const . A.Success . const ()
-- | Collect coverage data for the current isolate. The coverage data may be incomplete due to
-- garbage collection.
-- | Parameters of the 'Profiler.getBestEffortCoverage' command.
data PProfilerGetBestEffortCoverage = PProfilerGetBestEffortCoverage
deriving (Eq, Show)
pProfilerGetBestEffortCoverage
:: PProfilerGetBestEffortCoverage
pProfilerGetBestEffortCoverage
= PProfilerGetBestEffortCoverage
instance ToJSON PProfilerGetBestEffortCoverage where
toJSON _ = A.Null
data ProfilerGetBestEffortCoverage = ProfilerGetBestEffortCoverage
{
-- | Coverage data for the current isolate.
profilerGetBestEffortCoverageResult :: [ProfilerScriptCoverage]
}
deriving (Eq, Show)
instance FromJSON ProfilerGetBestEffortCoverage where
parseJSON = A.withObject "ProfilerGetBestEffortCoverage" $ \o -> ProfilerGetBestEffortCoverage
<$> o A..: "result"
instance Command PProfilerGetBestEffortCoverage where
type CommandResponse PProfilerGetBestEffortCoverage = ProfilerGetBestEffortCoverage
commandName _ = "Profiler.getBestEffortCoverage"
-- | Changes CPU profiler sampling interval. Must be called before CPU profiles recording started.
-- | Parameters of the 'Profiler.setSamplingInterval' command.
data PProfilerSetSamplingInterval = PProfilerSetSamplingInterval
{
-- | New sampling interval in microseconds.
pProfilerSetSamplingIntervalInterval :: Int
}
deriving (Eq, Show)
pProfilerSetSamplingInterval
{-
-- | New sampling interval in microseconds.
-}
:: Int
-> PProfilerSetSamplingInterval
pProfilerSetSamplingInterval
arg_pProfilerSetSamplingIntervalInterval
= PProfilerSetSamplingInterval
arg_pProfilerSetSamplingIntervalInterval
instance ToJSON PProfilerSetSamplingInterval where
toJSON p = A.object $ catMaybes [
("interval" A..=) <$> Just (pProfilerSetSamplingIntervalInterval p)
]
instance Command PProfilerSetSamplingInterval where
type CommandResponse PProfilerSetSamplingInterval = ()
commandName _ = "Profiler.setSamplingInterval"
fromJSON = const . A.Success . const ()
-- | Parameters of the 'Profiler.start' command.
data PProfilerStart = PProfilerStart
deriving (Eq, Show)
pProfilerStart
:: PProfilerStart
pProfilerStart
= PProfilerStart
instance ToJSON PProfilerStart where
toJSON _ = A.Null
instance Command PProfilerStart where
type CommandResponse PProfilerStart = ()
commandName _ = "Profiler.start"
fromJSON = const . A.Success . const ()
-- | Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code
-- coverage may be incomplete. Enabling prevents running optimized code and resets execution
-- counters.
-- | Parameters of the 'Profiler.startPreciseCoverage' command.
data PProfilerStartPreciseCoverage = PProfilerStartPreciseCoverage
{
-- | Collect accurate call counts beyond simple 'covered' or 'not covered'.
pProfilerStartPreciseCoverageCallCount :: Maybe Bool,
-- | Collect block-based coverage.
pProfilerStartPreciseCoverageDetailed :: Maybe Bool,
-- | Allow the backend to send updates on its own initiative
pProfilerStartPreciseCoverageAllowTriggeredUpdates :: Maybe Bool
}
deriving (Eq, Show)
pProfilerStartPreciseCoverage
:: PProfilerStartPreciseCoverage
pProfilerStartPreciseCoverage
= PProfilerStartPreciseCoverage
Nothing
Nothing
Nothing
instance ToJSON PProfilerStartPreciseCoverage where
toJSON p = A.object $ catMaybes [
("callCount" A..=) <$> (pProfilerStartPreciseCoverageCallCount p),
("detailed" A..=) <$> (pProfilerStartPreciseCoverageDetailed p),
("allowTriggeredUpdates" A..=) <$> (pProfilerStartPreciseCoverageAllowTriggeredUpdates p)
]
data ProfilerStartPreciseCoverage = ProfilerStartPreciseCoverage
{
-- | Monotonically increasing time (in seconds) when the coverage update was taken in the backend.
profilerStartPreciseCoverageTimestamp :: Double
}
deriving (Eq, Show)
instance FromJSON ProfilerStartPreciseCoverage where
parseJSON = A.withObject "ProfilerStartPreciseCoverage" $ \o -> ProfilerStartPreciseCoverage
<$> o A..: "timestamp"
instance Command PProfilerStartPreciseCoverage where
type CommandResponse PProfilerStartPreciseCoverage = ProfilerStartPreciseCoverage
commandName _ = "Profiler.startPreciseCoverage"
-- | Parameters of the 'Profiler.stop' command.
data PProfilerStop = PProfilerStop
deriving (Eq, Show)
pProfilerStop
:: PProfilerStop
pProfilerStop
= PProfilerStop
instance ToJSON PProfilerStop where
toJSON _ = A.Null
data ProfilerStop = ProfilerStop
{
-- | Recorded profile.
profilerStopProfile :: ProfilerProfile
}
deriving (Eq, Show)
instance FromJSON ProfilerStop where
parseJSON = A.withObject "ProfilerStop" $ \o -> ProfilerStop
<$> o A..: "profile"
instance Command PProfilerStop where
type CommandResponse PProfilerStop = ProfilerStop
commandName _ = "Profiler.stop"
-- | Disable precise code coverage. Disabling releases unnecessary execution count records and allows
-- executing optimized code.
-- | Parameters of the 'Profiler.stopPreciseCoverage' command.
data PProfilerStopPreciseCoverage = PProfilerStopPreciseCoverage
deriving (Eq, Show)
pProfilerStopPreciseCoverage
:: PProfilerStopPreciseCoverage
pProfilerStopPreciseCoverage
= PProfilerStopPreciseCoverage
instance ToJSON PProfilerStopPreciseCoverage where
toJSON _ = A.Null
instance Command PProfilerStopPreciseCoverage where
type CommandResponse PProfilerStopPreciseCoverage = ()
commandName _ = "Profiler.stopPreciseCoverage"
fromJSON = const . A.Success . const ()
-- | Collect coverage data for the current isolate, and resets execution counters. Precise code
-- coverage needs to have started.
-- | Parameters of the 'Profiler.takePreciseCoverage' command.
data PProfilerTakePreciseCoverage = PProfilerTakePreciseCoverage
deriving (Eq, Show)
pProfilerTakePreciseCoverage
:: PProfilerTakePreciseCoverage
pProfilerTakePreciseCoverage
= PProfilerTakePreciseCoverage
instance ToJSON PProfilerTakePreciseCoverage where
toJSON _ = A.Null
data ProfilerTakePreciseCoverage = ProfilerTakePreciseCoverage
{
-- | Coverage data for the current isolate.
profilerTakePreciseCoverageResult :: [ProfilerScriptCoverage],
-- | Monotonically increasing time (in seconds) when the coverage update was taken in the backend.
profilerTakePreciseCoverageTimestamp :: Double
}
deriving (Eq, Show)
instance FromJSON ProfilerTakePreciseCoverage where
parseJSON = A.withObject "ProfilerTakePreciseCoverage" $ \o -> ProfilerTakePreciseCoverage
<$> o A..: "result"
<*> o A..: "timestamp"
instance Command PProfilerTakePreciseCoverage where
type CommandResponse PProfilerTakePreciseCoverage = ProfilerTakePreciseCoverage
commandName _ = "Profiler.takePreciseCoverage"