linux-perf-0.3: Profiling/Linux/Perf/Types.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-----------------------------------------------------------------------------
-- |
-- Copyright : (c) 2010,2011,2012 Simon Marlow, Bernie Pope, Mikolaj Konarski
-- License : BSD-style
-- Maintainer : florbitous@gmail.com
-- Stability : experimental
-- Portability : ghc
--
-- Types for representing the parsed contents of a @perf.data@ file output
-- the @perf record@ command on Linux (Linux performance counter information).
--
-- There is an intentional close correspondence between the types in this
-- module and the representation in the C implementation of perf.
--
-----------------------------------------------------------------------------
module Profiling.Linux.Perf.Types
( -- * Identifiers
PID (..)
, TID (..)
, EventID (..)
, EventTypeID (..)
-- * Byte counters
, ByteCount64 (..)
, ByteCount32 (..)
, ByteCount16 (..)
-- * Timestamps
, TimeStamp (..)
-- * File structure
, FileSection (..)
, FileHeader (..)
, FileAttr (..)
-- * Event data
, Event (..)
, EventCPUMode (..)
, EventAttr (..)
, EventHeader (..)
, EventPayload (..)
-- * Event type information
, EventType (..)
, SampleFormat (..)
, TraceEventType (..)
, EventAttrFlag (..)
, EventSource (..)
, SampleTypeBitMap (..)
, testEventAttrFlag
-- * An abstract representation of the perf data file
, PerfData (..)
) where
import Data.Word (Word64, Word32, Word16, Word8, Word)
import Text.PrettyPrint (text, (<+>), ($$), render, empty, integer, (<>), hsep, Doc)
import Data.ByteString.Lazy (ByteString)
import Profiling.Linux.Perf.Pretty (Pretty (..), showBits)
import Data.Bits (testBit)
-- -----------------------------------------------------------------------------
-- | The various parts of the perf.data file collected together.
data PerfData =
PerfData
{ perfData_fileHeader :: FileHeader -- ^ File header explains the structure of the file.
, perfData_attrs :: [FileAttr] -- ^ Common attributes of events.
, perfData_idss :: [[EventID]] -- ^ Event identifiers to be associated with the event attributes.
, perfData_types :: [TraceEventType] -- ^ Event type information.
, perfData_events :: [Event] -- ^ The event payload.
}
-- | Process ID.
newtype PID = PID { pid :: Word32 }
deriving (Eq, Ord, Show, Pretty)
-- | Thread ID.
newtype TID = TID { tid :: Word32 }
deriving (Eq, Ord, Show, Pretty)
-- | Event type ID (magic unique number of an event type).
newtype EventTypeID = EventTypeID { eventTypeID :: Word64 }
deriving (Eq, Ord, Show, Pretty)
-- | Event ID.
-- Not really an identity. This number is used to link
-- an event to an event type. Multiple events can have the same EventID,
-- which means they all have the same event type.
newtype EventID = EventID { eventID :: Word64 }
deriving (Eq, Ord, Show, Pretty)
-- | Measurement of time passed in nanoseconds since a given point.
newtype TimeStamp = TimeStamp { timeStamp :: Word64 }
deriving (Eq, Ord, Show, Pretty)
-- | A bitmap encoding information about the content of sample events.
newtype SampleTypeBitMap = SampleTypeBitMap { sampleTypeBitMap :: Word64 }
deriving (Eq, Show, Pretty)
-- | A 64 bit measurement in bytes. For example the size of an object, or an offset.
newtype ByteCount64 = ByteCount64 { byteCount64 :: Word64 }
deriving (Eq, Ord, Show, Pretty, Enum, Integral, Real, Num)
-- | A 32 bit measurement in bytes. For example the size of an object, or an offset.
newtype ByteCount32 = ByteCount32 { byteCount32 :: Word32 }
deriving (Eq, Ord, Show, Pretty, Enum, Integral, Real, Num)
-- | A 16 bit measurement in bytes. For example the size of an object, or an offset.
newtype ByteCount16 = ByteCount16 { byteCount16 :: Word16 }
deriving (Eq, Ord, Show, Pretty, Enum, Integral, Real, Num)
-- | A Single event record.
data Event =
Event
{ ev_header :: EventHeader -- ^ Information about the structure of the event.
, ev_payload :: EventPayload -- ^ The event data.
}
instance Pretty Event where
pretty ev@(Event {}) =
text "header: " <+> (pretty $ ev_header ev) $$
text "payload: " <+> (pretty $ ev_payload ev)
-- XXX should probably get this from the definintion of the C type
-- | Encoding of the @perf_event_header->type@.
data EventType
= PERF_RECORD_MMAP -- 1
| PERF_RECORD_LOST -- 2
| PERF_RECORD_COMM -- 3
| PERF_RECORD_EXIT -- 4
| PERF_RECORD_THROTTLE -- 5
| PERF_RECORD_UNTHROTTLE -- 6
| PERF_RECORD_FORK -- 7
| PERF_RECORD_READ -- 8
| PERF_RECORD_SAMPLE -- 9
| PERF_RECORD_UNKNOWN Int -- possibly a bad number?
deriving (Eq, Show)
instance Enum EventType where
toEnum 1 = PERF_RECORD_MMAP
toEnum 2 = PERF_RECORD_LOST
toEnum 3 = PERF_RECORD_COMM
toEnum 4 = PERF_RECORD_EXIT
toEnum 5 = PERF_RECORD_THROTTLE
toEnum 6 = PERF_RECORD_UNTHROTTLE
toEnum 7 = PERF_RECORD_FORK
toEnum 8 = PERF_RECORD_READ
toEnum 9 = PERF_RECORD_SAMPLE
toEnum other = PERF_RECORD_UNKNOWN other
fromEnum PERF_RECORD_MMAP = 1
fromEnum PERF_RECORD_LOST = 2
fromEnum PERF_RECORD_COMM = 3
fromEnum PERF_RECORD_EXIT = 4
fromEnum PERF_RECORD_THROTTLE = 5
fromEnum PERF_RECORD_UNTHROTTLE = 6
fromEnum PERF_RECORD_FORK = 7
fromEnum PERF_RECORD_READ = 8
fromEnum PERF_RECORD_SAMPLE = 9
fromEnum (PERF_RECORD_UNKNOWN other) = other
instance Pretty EventType where
pretty = text . show
-- | Information about what is stored in a sample event.
data SampleFormat
= PERF_SAMPLE_IP -- ^ 1U << 0
| PERF_SAMPLE_TID -- ^ 1U << 1
| PERF_SAMPLE_TIME -- ^ 1U << 2
| PERF_SAMPLE_ADDR -- ^ 1U << 3
| PERF_SAMPLE_READ -- ^ 1U << 4
| PERF_SAMPLE_CALLCHAIN -- ^ 1U << 5
| PERF_SAMPLE_ID -- ^ 1U << 6
| PERF_SAMPLE_CPU -- ^ 1U << 7
| PERF_SAMPLE_PERIOD -- ^ 1U << 8
| PERF_SAMPLE_STREAM_ID -- ^ 1U << 9
| PERF_SAMPLE_RAW -- ^ 1U << 10
deriving (Eq, Enum, Show)
instance Pretty SampleFormat where
pretty = text . show
-- | A bitfield in @perf_event_header->misc@.
data EventCPUMode
= PERF_RECORD_CPUMODE_UNKNOWN -- ^ 0
| PERF_RECORD_MISC_KERNEL -- ^ 1
| PERF_RECORD_MISC_USER -- ^ 2
| PERF_RECORD_MISC_HYPERVISOR -- ^ 3
deriving (Eq, Show)
instance Pretty EventCPUMode where
pretty = text . show
-- | Corresponds with the @perf_file_section@ struct in @\<perf source\>\/util\/header.h@
data FileSection
= FileSection {
sec_offset :: ByteCount64, -- ^ File offset to the section.
sec_size :: ByteCount64 -- ^ Size of the section in bytes.
}
instance Pretty FileSection where
pretty fs = text "offset:" <+> pretty (sec_offset fs) $$
text "size:" <+> pretty (sec_size fs)
-- | Corresponds with the @perf_file_header@ struct in @\<perf source\>\/util\/header.h@
data FileHeader
= FileHeader {
fh_size :: ByteCount64, -- ^ Size of (this) header.
fh_attr_size :: ByteCount64, -- ^ Size of one attribute section.
fh_attrs_offset :: ByteCount64, -- ^ File offset to the attribute section.
fh_attrs_size :: ByteCount64, -- ^ Size of the attribute section in bytes.
fh_data_offset :: ByteCount64, -- ^ File offset to the data section.
fh_data_size :: ByteCount64, -- ^ Size of the data section in bytes.
fh_event_offset :: ByteCount64, -- ^ File offset to the event section.
fh_event_size :: ByteCount64, -- ^ Size of the event section in bytes.
fh_adds_features :: [Word32] -- ^ Bitfield.
}
instance Pretty FileHeader where
pretty fh =
text "size:" <+> pretty (fh_size fh) $$
text "attribute size:" <+> pretty (fh_attr_size fh) $$
text "attributes offset:" <+> pretty (fh_attrs_offset fh) $$
text "attributes size:" <+> pretty (fh_attrs_size fh) $$
text "data offset:" <+> pretty (fh_data_offset fh) $$
text "data size:" <+> pretty (fh_data_size fh) $$
text "event offset:" <+> pretty (fh_event_offset fh) $$
text "event size:" <+> pretty (fh_event_size fh) $$
text "features:" <+> hsep (Prelude.map pretty $ fh_adds_features fh)
-- | See struct @perf_event_attr@ in @linux\/perf_event.h@
data EventAttrFlag
= Disabled -- ^ off by default
| Inherit -- ^ children inherit it
| Pinned -- ^ must always be on PMU
| Exclusive -- ^ only group on PMU
| ExcludeUser -- ^ don't count user
| ExcludeKernel -- ^ ditto kernel
| ExcludeHV -- ^ ditto hypervisor
| ExcludeIdle -- ^ don't count when idle
| Mmap -- ^ include mmap data
| Comm -- ^ include comm data
| Freq -- ^ use freq, not period
| InheritStat -- ^ per task counts
| EnableOnExec -- ^ next exec enables
| Task -- ^ trace fork/exit
| WaterMark -- ^ wakeup_watermark
| ArbitrarySkid -- ^ precise_ip, See also @PERF_RECORD_MISC_EXACT_IP@
| ConstantSkid -- ^ precise_ip, See also @PERF_RECORD_MISC_EXACT_IP@
| RequestedZeroSkid -- ^ precise_ip, See also @PERF_RECORD_MISC_EXACT_IP@
| CompulsoryZeroSkid -- ^ precise_ip, See also @PERF_RECORD_MISC_EXACT_IP@
| MmapData -- ^ non-exec mmap data
| SampleIdAll -- ^ sample_type all events
deriving (Eq, Ord, Enum, Show)
instance Pretty EventAttrFlag where
pretty Disabled = text "disabled"
pretty Inherit = text "inherit"
pretty Pinned = text "pinned"
pretty Exclusive = text "exclusive"
pretty ExcludeUser = text "exclude-user"
pretty ExcludeKernel = text "exclude-kernel"
pretty ExcludeHV = text "exclude-hypervisor"
pretty ExcludeIdle = text "exclude-idle"
pretty Mmap = text "mmap"
pretty Comm = text "comm"
pretty Freq = text "freq"
pretty InheritStat = text "inherit-stat"
pretty EnableOnExec = text "enable-on-exec"
pretty Task = text "task"
pretty WaterMark = text "watermark"
pretty ArbitrarySkid = text "arbitrary-skid"
pretty ConstantSkid = text "constant-skid"
pretty RequestedZeroSkid = text "requested-zero-skid"
pretty CompulsoryZeroSkid = text "compulsory-zero-skid"
pretty MmapData = text "mmap-data"
pretty SampleIdAll = text "sample-id-all"
-- | Test if a given EventAttrFlag is set.
-- Tedious definition because of the way the skid flags are
-- implemented as a 2 bit word instead of individual single bits.
testEventAttrFlag :: Word64 -> EventAttrFlag -> Bool
testEventAttrFlag word flag =
case flag of
Disabled -> testBit word 0
Inherit -> testBit word 1
Pinned -> testBit word 2
Exclusive -> testBit word 3
ExcludeUser -> testBit word 4
ExcludeKernel -> testBit word 5
ExcludeHV -> testBit word 6
ExcludeIdle -> testBit word 7
Mmap -> testBit word 8
Comm -> testBit word 9
Freq -> testBit word 10
InheritStat -> testBit word 11
EnableOnExec -> testBit word 12
Task -> testBit word 13
WaterMark -> testBit word 14
ArbitrarySkid -> not (testBit word 15) && not (testBit word 16)
ConstantSkid -> not (testBit word 15) && (testBit word 16)
RequestedZeroSkid -> (testBit word 15) && not (testBit word 16)
CompulsoryZeroSkid -> (testBit word 15) && (testBit word 16)
MmapData -> testBit word 17
SampleIdAll -> testBit word 18
-- | Pretty print an event attribute bit field as a list of set flags.
prettyFlags :: Word64 -> Doc
prettyFlags word = foldr testFlag empty [toEnum 0 ..]
where
testFlag :: EventAttrFlag -> Doc -> Doc
testFlag flag rest
| testEventAttrFlag word flag = pretty flag <+> rest
| otherwise = rest
-- | Corresponds with the enum @perf_type_id@ in @include\/linux\/perf_event.h@
-- XXX should really derive this directly from the header file
data EventSource
= PerfTypeHardware -- ^ 0
| PerfTypeSoftware -- ^ 1
| PerfTypeTracePoint -- ^ 2
| PerfTypeHwCache -- ^ 3
| PerfTypeRaw -- ^ 4
| PerfTypeBreakpoint -- ^ 5
| PerfTypeUnknown
deriving (Eq, Ord, Show, Enum)
instance Pretty EventSource where
pretty = text . show
-- | Corresponds with the @perf_event_attr@ struct in @include\/linux\/perf_event.h@
data EventAttr
= EventAttr {
ea_type :: EventSource, -- ^ Major type: hardware/software/tracepoint/etc.
-- defined as enum perf_type_id in include/linux/perf_event.h
ea_size :: ByteCount32, -- ^ Size of the attr structure, for fwd/bwd compat.
ea_config :: EventTypeID, -- ^ Link to .event id of perf trace event type.
ea_sample_period_or_freq :: Word64, -- ^ Number of events when a sample is generated if .freq
-- is not set or frequency for sampling if .freq is set.
ea_sample_type :: SampleTypeBitMap, -- ^ Information about what is stored in the sampling record.
ea_read_format :: Word64, --
ea_flags :: Word64, --
ea_wakeup_events_or_watermark :: Word32, -- ^ Wakeup every n events or bytes before wakeup.
ea_bp_type :: Word32, --
ea_bp_addr_or_config1 :: Word64, --
ea_bp_len_or_config2 :: Word64 --
}
instance Pretty EventAttr where
pretty ea =
text "type:" <+> pretty (ea_type ea) $$
text "size:" <+> pretty (ea_size ea) $$
text "config:" <+> pretty (ea_config ea) $$
text "sample period or frequency:" <+> pretty (ea_sample_period_or_freq ea) $$
text "sample type:" <+> pretty (ea_sample_type ea) $$
text "read format:" <+> pretty (ea_read_format ea) $$
text "flags:" <+> prettyFlags (ea_flags ea) $$
text "wakeup events or watermark:" <+> pretty (ea_wakeup_events_or_watermark ea) $$
text "bp type:" <+> pretty (ea_bp_type ea) $$
text "bp address or config1:" <+> pretty (ea_bp_addr_or_config1 ea) $$
text "bp length or config2:" <+> pretty (ea_bp_len_or_config2 ea)
-- | Layout of event attribute and attribute ids.
data FileAttr = FileAttr {
fa_attr :: EventAttr, -- ^ The attribute payload.
fa_ids_offset :: ByteCount64, -- ^ File offset to the ids section.
fa_ids_size :: ByteCount64 -- ^ Size of the ids section in bytes.
}
instance Pretty FileAttr where
pretty fa =
text "event attribute:" <+> pretty (fa_attr fa) $$
text "ids offset:" <+> pretty (fa_ids_offset fa) $$
text "ids size:" <+> pretty (fa_ids_size fa)
-- | Identity and printable name of an event type.
data TraceEventType = TraceEventType {
te_event_id :: EventTypeID, -- ^ This entry belongs to the perf event attr entry where .config
-- has the same value as this id.
te_name :: ByteString
}
instance Pretty TraceEventType where
pretty te =
text "event id:" <+> pretty (te_event_id te) $$
text "name:" <+> pretty (te_name te)
-- | Corresponds with the @perf_event_header@ struct in @\<perf source\>\/util\/perf_event.h@
data EventHeader = EventHeader {
eh_type :: EventType,
eh_misc :: Word16,
eh_size :: ByteCount16
}
instance Pretty EventHeader where
pretty eh =
text "type:" <+> pretty (eh_type eh) $$
text "misc:" <+> pretty (eh_misc eh) $$
text "size:" <+> pretty (eh_size eh)
data EventPayload =
-- Corresponds with the @comm_event@ struct in @\<perf source\>\/util\/event.h@ (without the header).
CommEvent {
eventPayload_pid :: PID, -- ^ process id
eventPayload_tid :: TID, -- ^ thread id
eventPayload_CommName :: ByteString -- ^ name of the application
}
-- Corresponds with the @mmap_event@ struct in @\<perf source\>\/util\/event.h@ (without the header).
| MmapEvent {
eventPayload_pid :: PID,
eventPayload_tid :: TID,
eventPayload_MmapStart :: Word64, -- ^ start of memory range
eventPayload_MmapLen :: Word64, -- ^ size of memory range
eventPayload_MmapPgoff :: Word64, -- ^ page offset
eventPayload_MmapFilename :: ByteString -- ^ binary file using this range
}
-- ForkEvent corresponds with the @fork_event@ struct in @\<perf source\>\/util\/event.h@ (without the header)
| ForkEvent {
eventPayload_pid :: PID,
eventPayload_ppid :: PID, -- ^ parent proecess id
eventPayload_tid :: TID,
eventPayload_ptid :: TID, -- ^ parent thread id
eventPayload_time :: TimeStamp -- ^ timestamp
}
-- Corresponds with the @exit_event@ struct in @\<perf source\>\/util\/event.h@ (without the header)
| ExitEvent {
eventPayload_pid :: PID,
eventPayload_ppid :: PID,
eventPayload_tid :: TID,
eventPayload_ptid :: TID,
eventPayload_time :: TimeStamp
}
-- Corresponds with the @lost_event@ struct in @\<perf source\>\/util\/event.h@ (without the header)
| LostEvent {
eventPayload_id :: EventID,
eventPayload_Lost :: Word64
}
-- Corresponds with the @read_event@ struct in @\<perf source\>\/util\/event.h@ (without the header)
| ReadEvent {
eventPayload_pid :: PID,
eventPayload_tid :: TID,
eventPayload_ReadValue :: Word64,
eventPayload_ReadTimeEnabled :: Word64,
eventPayload_ReadTimeRunning :: Word64,
eventPayload_id :: EventID
}
| SampleEvent {
eventPayload_SampleIP :: Maybe Word64, -- ^ Instruction pointer.
eventPayload_SamplePID :: Maybe PID, -- ^ Process ID.
eventPayload_SampleTID :: Maybe TID, -- ^ Thread ID.
eventPayload_SampleTime :: Maybe TimeStamp, -- ^ Timestamp.
eventPayload_SampleAddr :: Maybe Word64, --
eventPayload_SampleID :: Maybe EventID, -- ^ Event ID.
eventPayload_SampleStreamID :: Maybe Word64, --
eventPayload_SampleCPU :: Maybe Word32, -- ^ CPU ID.
eventPayload_SamplePeriod :: Maybe Word64 -- ^ Duration of sample.
}
-- ThrottleEvent and UnThrottleEvent are mentioned in
-- @\<system include directory\>\/linux\/perf_event.h@
-- but does not appear in @\<perf source\>\/util\/event.h@
| ThrottleEvent {
eventPayload_time :: TimeStamp,
eventPayload_id :: EventID,
eventPayload_stream_id :: Word64
}
| UnThrottleEvent {
eventPayload_time :: TimeStamp,
eventPayload_id :: EventID,
eventPayload_stream_id :: Word64
}
| UnknownEvent -- ^ An unrecognised event was encountered.
deriving (Show)
instance Pretty EventPayload where
pretty ce@(CommEvent{}) =
text "pid:" <+> pretty (eventPayload_pid ce) $$
text "tid:" <+> pretty (eventPayload_tid ce) $$
text "comm:" <+> pretty (eventPayload_CommName ce)
pretty me@(MmapEvent{}) =
text "pid:" <+> pretty (eventPayload_pid me) $$
text "tid:" <+> pretty (eventPayload_tid me) $$
text "start:" <+> pretty (eventPayload_MmapStart me) $$
text "len:" <+> pretty (eventPayload_MmapLen me) $$
text "pgoff:" <+> pretty (eventPayload_MmapPgoff me) $$
text "filename:" <+> pretty (eventPayload_MmapFilename me)
pretty fe@(ForkEvent{}) =
text "pid:" <+> pretty (eventPayload_pid fe) $$
text "ppid:" <+> pretty (eventPayload_ppid fe) $$
text "tid:" <+> pretty (eventPayload_tid fe) $$
text "ptid:" <+> pretty (eventPayload_ptid fe) $$
text "time:" <+> pretty (eventPayload_time fe)
pretty ee@(ExitEvent{}) =
text "pid:" <+> pretty (eventPayload_pid ee) $$
text "ppid:" <+> pretty (eventPayload_ppid ee) $$
text "tid:" <+> pretty (eventPayload_tid ee) $$
text "ptid:" <+> pretty (eventPayload_ptid ee) $$
text "time:" <+> pretty (eventPayload_time ee)
pretty le@(LostEvent {}) =
text "id:" <+> pretty (eventPayload_id le) $$
text "lost:" <+> pretty (eventPayload_Lost le)
pretty se@(SampleEvent {}) =
text "ip:" <+> pretty (eventPayload_SampleIP se) $$
text "pid:" <+> pretty (eventPayload_SamplePID se) $$
text "tid:" <+> pretty (eventPayload_SampleTID se) $$
text "time:" <+> pretty (eventPayload_SampleTime se) $$
text "addr:" <+> pretty (eventPayload_SampleAddr se) $$
text "id:" <+> pretty (eventPayload_SampleID se) $$
text "streamid:" <+> pretty (eventPayload_SampleStreamID se) $$
text "cpu:" <+> pretty (eventPayload_SampleCPU se) $$
text "period:" <+> pretty (eventPayload_SamplePeriod se)
pretty te@(ThrottleEvent {}) =
text "time:" <+> pretty (eventPayload_time te) $$
text "id:" <+> pretty (eventPayload_id te) $$
text "stream_id:" <+> pretty (eventPayload_stream_id te)
pretty ue@(UnThrottleEvent {}) =
text "time:" <+> pretty (eventPayload_time ue) $$
text "id:" <+> pretty (eventPayload_id ue) $$
text "stream_id:" <+> pretty (eventPayload_stream_id ue)
pretty UnknownEvent = text "Unknown"