use std::fmt;
use std::fs::File;
use std::io;
use std::io::{Error, Read};
use std::mem;
use std::os::unix::io::FromRawFd;
use std::ptr;
use std::slice;
use std::str;
use libc::{pid_t, strlen, MAP_SHARED};
use mmap;
use x86::*;
#[allow(dead_code, non_camel_case_types)]
mod hw_breakpoint;
#[allow(dead_code, non_camel_case_types)]
mod perf_event;
pub mod parser;
pub mod perf_file;
pub mod perf_format;
use self::perf_format::{EventAttrFlags, ReadFormatFlags, SampleFormatFlags};
use x86::perfcnt::intel::{EventDescription, Tuple};
use crate::AbstractPerfCounter;
const IOCTL: usize = 16;
const PERF_EVENT_OPEN: usize = 298;
fn perf_event_open(
hw_event: &perf_format::EventAttr,
pid: perf_event::__kernel_pid_t,
cpu: ::libc::c_int,
group_fd: ::libc::c_int,
flags: ::libc::c_int,
) -> isize {
unsafe {
syscall!(
PERF_EVENT_OPEN,
hw_event as *const perf_format::EventAttr as usize,
pid,
cpu,
group_fd,
flags
) as isize
}
}
fn ioctl(fd: ::libc::c_int, request: u64, value: ::libc::c_int) -> isize {
unsafe { syscall!(IOCTL, fd, request, value) as isize }
}
pub struct PerfCounterBuilderLinux {
group: isize,
pid: pid_t,
cpu: isize,
flags: i32,
attrs: perf_format::EventAttr,
}
impl Default for PerfCounterBuilderLinux {
fn default() -> PerfCounterBuilderLinux {
PerfCounterBuilderLinux {
group: -1,
pid: 0,
cpu: -1,
flags: 0,
attrs: Default::default(),
}
}
}
pub enum HardwareEventType {
CPUCycles = perf_event::PERF_COUNT_HW_CPU_CYCLES as isize,
Instructions = perf_event::PERF_COUNT_HW_INSTRUCTIONS as isize,
CacheReferences = perf_event::PERF_COUNT_HW_CACHE_REFERENCES as isize,
CacheMisses = perf_event::PERF_COUNT_HW_CACHE_MISSES as isize,
BranchInstructions = perf_event::PERF_COUNT_HW_BRANCH_INSTRUCTIONS as isize,
BranchMisses = perf_event::PERF_COUNT_HW_BRANCH_MISSES as isize,
BusCycles = perf_event::PERF_COUNT_HW_BUS_CYCLES as isize,
StalledCyclesFrontend = perf_event::PERF_COUNT_HW_STALLED_CYCLES_FRONTEND as isize,
StalledCyclesBackend = perf_event::PERF_COUNT_HW_STALLED_CYCLES_BACKEND as isize,
RefCPUCycles = perf_event::PERF_COUNT_HW_REF_CPU_CYCLES as isize,
}
pub enum SoftwareEventType {
CpuClock = perf_event::PERF_COUNT_SW_CPU_CLOCK as isize,
TaskClock = perf_event::PERF_COUNT_SW_TASK_CLOCK as isize,
PageFaults = perf_event::PERF_COUNT_SW_PAGE_FAULTS as isize,
ContextSwitches = perf_event::PERF_COUNT_SW_CONTEXT_SWITCHES as isize,
CpuMigrations = perf_event::PERF_COUNT_SW_CPU_MIGRATIONS as isize,
PageFaultsMin = perf_event::PERF_COUNT_SW_PAGE_FAULTS_MIN as isize,
PageFaultsMaj = perf_event::PERF_COUNT_SW_PAGE_FAULTS_MAJ as isize,
AlignmentFaults = perf_event::PERF_COUNT_SW_ALIGNMENT_FAULTS as isize,
EmulationFaults = perf_event::PERF_COUNT_SW_EMULATION_FAULTS as isize,
}
pub enum CacheId {
L1D = perf_event::PERF_COUNT_HW_CACHE_L1D as isize,
L1I = perf_event::PERF_COUNT_HW_CACHE_L1I as isize,
LL = perf_event::PERF_COUNT_HW_CACHE_LL as isize,
DTLB = perf_event::PERF_COUNT_HW_CACHE_DTLB as isize,
ITLB = perf_event::PERF_COUNT_HW_CACHE_ITLB as isize,
BPU = perf_event::PERF_COUNT_HW_CACHE_BPU as isize,
NODE = perf_event::PERF_COUNT_HW_CACHE_NODE as isize,
}
pub enum CacheOpId {
Read = perf_event::PERF_COUNT_HW_CACHE_OP_READ as isize,
Write = perf_event::PERF_COUNT_HW_CACHE_OP_WRITE as isize,
Prefetch = perf_event::PERF_COUNT_HW_CACHE_OP_PREFETCH as isize,
}
pub enum CacheOpResultId {
Access = perf_event::PERF_COUNT_HW_CACHE_RESULT_ACCESS as isize,
Miss = perf_event::PERF_COUNT_HW_CACHE_RESULT_MISS as isize,
}
impl PerfCounterBuilderLinux {
pub fn from_hardware_event(event: HardwareEventType) -> PerfCounterBuilderLinux {
let mut pc: PerfCounterBuilderLinux = Default::default();
pc.attrs.attr_type = perf_event::PERF_TYPE_HARDWARE;
pc.attrs.config = event as u64;
pc
}
pub fn from_software_event(event: SoftwareEventType) -> PerfCounterBuilderLinux {
let mut pc: PerfCounterBuilderLinux = Default::default();
pc.attrs.attr_type = perf_event::PERF_TYPE_SOFTWARE;
pc.attrs.config = event as u64;
pc
}
pub fn from_cache_event(
cache_id: CacheId,
cache_op_id: CacheOpId,
cache_op_result_id: CacheOpResultId,
) -> PerfCounterBuilderLinux {
let mut pc: PerfCounterBuilderLinux = Default::default();
pc.attrs.attr_type = perf_event::PERF_TYPE_HW_CACHE;
pc.attrs.config =
(cache_id as u64) | (cache_op_id as u64) << 8 | (cache_op_result_id as u64) << 16;
pc
}
pub fn from_intel_event_description(counter: &EventDescription) -> PerfCounterBuilderLinux {
let mut pc: PerfCounterBuilderLinux = Default::default();
let mut config: u64 = 0;
match counter.event_code {
Tuple::One(code) => config |= (code as u64) << 0,
Tuple::Two(_, _) => unreachable!(),
};
match counter.umask {
Tuple::One(code) => config |= (code as u64) << 8,
Tuple::Two(_, _) => unreachable!(),
};
config |= (counter.counter_mask as u64) << 24;
if counter.edge_detect {
config |= 1 << 18;
}
if counter.any_thread {
config |= 1 << 21;
}
if counter.invert {
config |= 1 << 23;
}
pc.attrs.attr_type = perf_event::PERF_TYPE_RAW;
pc.attrs.config = config;
pc
}
pub fn set_group<'a>(&'a mut self, group_fd: isize) -> &'a mut PerfCounterBuilderLinux {
self.group = group_fd;
self
}
pub fn set_flag_fd_output<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.flags |= 0x02;
self
}
pub fn set_flag_pid_cgroup<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.flags |= 0x04;
self
}
pub fn set_sample_period<'a>(&'a mut self, period: u64) -> &'a mut PerfCounterBuilderLinux {
self.attrs.sample_period_freq = period;
self
}
pub fn set_sample_frequency<'a>(
&'a mut self,
frequency: u64,
) -> &'a mut PerfCounterBuilderLinux {
self.attrs.sample_period_freq = frequency;
self.attrs.settings.insert(EventAttrFlags::EVENT_ATTR_FREQ);
self
}
pub fn disable<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.settings.insert(EventAttrFlags::EVENT_ATTR_DISABLED);
self
}
pub fn inherit<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.settings.insert(EventAttrFlags::EVENT_ATTR_INHERIT);
self
}
pub fn pinned<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.settings.insert(EventAttrFlags::EVENT_ATTR_PINNED);
self
}
pub fn exclusive<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_EXCLUSIVE);
self
}
pub fn exclude_user<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_EXCLUDE_USER);
self
}
pub fn exclude_kernel<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_EXCLUDE_KERNEL);
self
}
pub fn exclude_hv<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_EXCLUDE_HV);
self
}
pub fn exclude_idle<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_EXCLUDE_IDLE);
self
}
pub fn enable_mmap<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.settings.insert(EventAttrFlags::EVENT_ATTR_MMAP);
self
}
pub fn inherit_stat<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_INHERIT_STAT);
self
}
pub fn enable_on_exec<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_ENABLE_ON_EXEC);
self
}
pub fn enable_task_notification<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.settings.insert(EventAttrFlags::EVENT_ATTR_TASK);
self
}
pub fn enable_watermark<'a>(
&'a mut self,
watermark_events: u32,
) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_WATERMARK);
self.attrs.wakeup_events_watermark = watermark_events;
self
}
pub fn set_ip_sample_arbitrary_skid<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_SAMPLE_IP_ARBITRARY_SKID);
self
}
pub fn set_ip_sample_constant_skid<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_SAMPLE_IP_CONSTANT_SKID);
self
}
pub fn set_ip_sample_req_zero_skid<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_SAMPLE_IP_REQ_ZERO_SKID);
self
}
pub fn enable_mmap_data<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_MMAP_DATA);
self
}
pub fn set_ip_sample_zero_skid<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.settings
.insert(EventAttrFlags::EVENT_ATTR_SAMPLE_IP_ZERO_SKID);
self
}
pub fn enable_read_format_time_enabled<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.read_format
.insert(ReadFormatFlags::FORMAT_TOTAL_TIME_ENABLED);
self
}
pub fn enable_read_format_time_running<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs
.read_format
.insert(ReadFormatFlags::FORMAT_TOTAL_TIME_RUNNING);
self
}
pub fn enable_read_format_id<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.read_format.insert(ReadFormatFlags::FORMAT_ID);
self
}
pub fn enable_read_format_group<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.attrs.read_format.insert(ReadFormatFlags::FORMAT_GROUP);
self
}
pub fn enable_sampling_ip<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_IP);
self
}
pub fn enable_sampling_tid<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_TID);
self
}
pub fn enable_sampling_time<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_TIME);
self
}
pub fn enable_sampling_addr<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_ADDR);
self
}
pub fn enable_sampling_read<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_READ);
self
}
pub fn enable_sampling_callchain<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_CALLCHAIN);
self
}
pub fn enable_sampling_sample_id<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_ID);
self
}
pub fn enable_sampling_cpu<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_CPU);
self
}
pub fn enable_sampling_period<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_PERIOD);
self
}
pub fn enable_sampling_stream_id<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_STREAM_ID);
self
}
pub fn enable_sampling_raw<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs.sample_type.insert(SampleFormatFlags::PERF_SAMPLE_RAW);
self
}
pub fn enable_sampling_branch_stack<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_BRANCH_STACK);
self
}
pub fn enable_sampling_regs_user<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_REGS_USER);
self
}
pub fn enable_sampling_stack_user<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_STACK_USER);
self
}
pub fn enable_sampling_sample_weight<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_WEIGHT);
self
}
pub fn enable_sampling_data_src<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_DATA_SRC);
self
}
pub fn enable_sampling_identifier<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_IDENTIFIER);
self
}
pub fn enable_sampling_transaction<'a>(&'a mut self) -> &'a PerfCounterBuilderLinux {
self.attrs
.sample_type
.insert(SampleFormatFlags::PERF_SAMPLE_TRANSACTION);
self
}
pub fn for_all_pids<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.pid = -1;
self
}
pub fn for_pid<'a>(&'a mut self, pid: i32) -> &'a mut PerfCounterBuilderLinux {
self.pid = pid;
self
}
pub fn on_cpu<'a>(&'a mut self, cpu: isize) -> &'a mut PerfCounterBuilderLinux {
self.cpu = cpu;
self
}
pub fn on_all_cpus<'a>(&'a mut self) -> &'a mut PerfCounterBuilderLinux {
self.cpu = -1;
self
}
pub fn finish_sampling_counter(&self) -> Result<PerfCounter, io::Error> {
let fd = perf_event_open(
&self.attrs,
self.pid,
self.cpu as i32,
self.group as i32,
self.flags,
) as ::libc::c_int;
if fd < 0 {
return Err(Error::from_raw_os_error(-fd));
}
Ok(PerfCounter {
fd: fd,
file: unsafe { File::from_raw_fd(fd) },
attributes: self.attrs,
})
}
pub fn finish(&self) -> Result<PerfCounter, io::Error> {
let fd = perf_event_open(
&self.attrs,
self.pid,
self.cpu as i32,
self.group as i32,
self.flags,
) as ::libc::c_int;
if fd < 0 {
return Err(Error::from_raw_os_error(-fd));
}
Ok(PerfCounter {
fd: fd,
file: unsafe { File::from_raw_fd(fd) },
attributes: self.attrs,
})
}
}
#[repr(C)]
#[derive(Default, Debug)]
pub struct FileReadFormat {
pub value: u64,
pub time_enabled: u64,
pub time_running: u64,
pub id: u64,
}
impl FileReadFormat {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> FileReadFormat {
let value: u64 = read(ptr, 0);
let time_enabled: u64 = read(ptr, 8);
let time_running: u64 = read(ptr, 16);
let id: u64 = read(ptr, 24);
FileReadFormat {
value: value,
time_enabled: time_enabled,
time_running: time_running,
id: id,
}
}
}
#[repr(C)]
pub struct MMAPPage {
version: u32,
compat_version: u32,
lock: u32,
index: u32,
offset: i64,
time_enabled: u64,
time_running: u64,
capabilities: u64,
pmc_width: u16,
time_shift: u16,
time_mult: u32,
time_offset: u64,
reserved: [u64; 120],
data_head: u64,
data_tail: u64,
}
impl fmt::Debug for MMAPPage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MMAPPage {{ version: {} compat_version: {} lock: {} index: {} offset: {} time_enabled: {} time_running: {} capabilities: {} pmc_width: {} time_shift: {} time_mult: {} time_offset: {} data_head: {} data_tail: {} }}",
self.version, self.compat_version, self.lock,
self.index, self.offset, self.time_enabled, self.time_running,
self.capabilities, self.pmc_width, self.time_shift, self.time_mult,
self.time_offset, self.data_head, self.data_tail)
}
}
pub struct PerfCounter {
fd: ::libc::c_int,
file: File,
attributes: perf_format::EventAttr,
}
impl PerfCounter {
pub fn read_fd(&mut self) -> Result<FileReadFormat, io::Error> {
unsafe {
let mut value: FileReadFormat = Default::default();
let ptr = mem::transmute::<&mut FileReadFormat, &mut u8>(&mut value);
let slice = slice::from_raw_parts_mut::<u8>(ptr, mem::size_of::<FileReadFormat>());
self.file.read_exact(slice)?;
Ok(value)
}
}
}
impl<'a> AbstractPerfCounter for PerfCounter {
fn reset(&self) -> Result<(), io::Error> {
let ret = ioctl(self.fd, perf_event::PERF_EVENT_IOC_RESET, 0);
if ret == -1 {
return Err(Error::last_os_error());
}
Ok(())
}
fn start(&self) -> Result<(), io::Error> {
let ret = ioctl(self.fd, perf_event::PERF_EVENT_IOC_ENABLE, 0);
if ret == -1 {
return Err(Error::last_os_error());
}
Ok(())
}
fn stop(&self) -> Result<(), io::Error> {
let ret = ioctl(self.fd, perf_event::PERF_EVENT_IOC_DISABLE, 0);
if ret == -1 {
return Err(Error::last_os_error());
}
Ok(())
}
fn read(&mut self) -> Result<u64, io::Error> {
let value: FileReadFormat = self.read_fd()?;
return Ok(value.value);
}
}
pub struct SamplingPerfCounter {
pc: PerfCounter,
map: mmap::MemoryMap,
events_size: usize,
}
unsafe fn read<U: Copy>(ptr: *const u8, offset: isize) -> U {
let newptr = mem::transmute::<*const u8, *const U>(ptr.offset(offset));
ptr::read(newptr)
}
#[derive(Default, Debug)]
struct EventHeader {
event_type: u32,
misc: u16,
size: u16,
}
impl EventHeader {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> EventHeader {
let event_type: u32 = read(ptr, 0);
let misc: u16 = read(ptr, 4);
let size: u16 = read(ptr, 6);
EventHeader {
event_type: event_type,
misc: misc,
size: size,
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct MMAPRecord {
header: EventHeader,
pid: u32,
tid: u32,
addr: u64,
len: u64,
pgoff: u64,
filename: String,
}
impl MMAPRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> MMAPRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let pid: u32 = read(ptr, 8);
let tid: u32 = read(ptr, 12);
let addr: u64 = read(ptr, 16);
let len: u64 = read(ptr, 24);
let pgoff: u64 = read(ptr, 32);
let filename = {
let str_start = ptr.offset(40);
let strlen_ptr = mem::transmute::<*const u8, &i8>(str_start);
let length = strlen(strlen_ptr) as usize;
let slice = slice::from_raw_parts(str_start, length);
String::from(str::from_utf8(slice).unwrap())
};
MMAPRecord {
header: header,
pid: pid,
tid: tid,
addr: addr,
len: len,
pgoff: pgoff,
filename: filename,
}
}
}
#[derive(Debug)]
pub struct LostRecord {
header: EventHeader,
id: u64,
lost: u64,
}
impl LostRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> LostRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let id: u64 = read(ptr, 8);
let lost: u64 = read(ptr, 16);
LostRecord {
header: header,
id: id,
lost: lost,
}
}
}
#[derive(Debug)]
pub struct CommRecord {
header: EventHeader,
pid: u32,
tid: u32,
comm: String,
}
impl CommRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> CommRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let pid: u32 = read(ptr, 8);
let tid: u32 = read(ptr, 12);
let comm = {
let str_start = ptr.offset(16);
let strlen_ptr = mem::transmute::<*const u8, &i8>(str_start);
let length = strlen(strlen_ptr) as usize;
let slice = slice::from_raw_parts(str_start, length);
String::from(str::from_utf8(slice).unwrap())
};
CommRecord {
header: header,
pid: pid,
tid: tid,
comm: comm,
}
}
}
#[derive(Debug)]
pub struct ExitRecord {
header: EventHeader,
pid: u32,
ppid: u32,
tid: u32,
ptid: u32,
time: u64,
}
impl ExitRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> ExitRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let pid: u32 = read(ptr, 8);
let ppid: u32 = read(ptr, 12);
let tid: u32 = read(ptr, 16);
let ptid: u32 = read(ptr, 20);
let time: u64 = read(ptr, 24);
ExitRecord {
header: header,
pid: pid,
ppid: ppid,
tid: tid,
ptid: ptid,
time: time,
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ThrottleRecord {
header: EventHeader,
time: u64,
id: u64,
stream_id: u64,
}
impl ThrottleRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> ThrottleRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let time: u64 = read(ptr, 8);
let id: u64 = read(ptr, 16);
let stream_id: u64 = read(ptr, 24);
ThrottleRecord {
header: header,
time: time,
id: id,
stream_id: stream_id,
}
}
}
#[derive(Debug)]
pub struct ForkRecord {
header: EventHeader,
pid: u32,
ppid: u32,
tid: u32,
ptid: u32,
time: u64,
}
impl ForkRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> ForkRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let pid: u32 = read(ptr, 8);
let ppid: u32 = read(ptr, 12);
let tid: u32 = read(ptr, 16);
let ptid: u32 = read(ptr, 20);
let time: u64 = read(ptr, 24);
ForkRecord {
header: header,
pid: pid,
ppid: ppid,
tid: tid,
ptid: ptid,
time: time,
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ReadRecord {
header: EventHeader,
pid: u32,
tid: u32,
value: FileReadFormat,
}
impl ReadRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> ReadRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let pid: u32 = read(ptr, 8);
let tid: u32 = read(ptr, 12);
let frf: FileReadFormat = FileReadFormat::copy_from_raw_ptr(ptr.offset(16));
ReadRecord {
header: header,
pid: pid,
tid: tid,
value: frf,
}
}
}
#[derive(Debug)]
struct BranchEntry {
pub from: u64,
pub to: u64,
flags: u64,
}
#[derive(Debug)]
pub struct SampleRecord {
header: EventHeader,
ip: u64,
pid: u32,
tid: u32,
time: u64,
addr: u64,
id: u64,
stream_id: u64,
cpu: u32,
res: u32,
period: u64,
v: FileReadFormat,
ips: Vec<u64>,
raw_sample: Vec<u8>,
lbr: Vec<BranchEntry>,
abi: u64,
regs: Vec<u64>,
user_stack: Vec<u8>,
dyn_size: u64,
weight: u64,
data_str: u64,
}
impl SampleRecord {
unsafe fn copy_from_raw_ptr(ptr: *const u8) -> SampleRecord {
let header: EventHeader = EventHeader::copy_from_raw_ptr(ptr);
let ip: u64 = read(ptr, 8);
let pid: u32 = read(ptr, 16);
let tid: u32 = read(ptr, 20);
let time: u64 = read(ptr, 24);
let addr: u64 = read(ptr, 32);
let id: u64 = read(ptr, 40);
let stream_id: u64 = read(ptr, 48);
let cpu: u32 = read(ptr, 52);
let res: u32 = read(ptr, 56);
let period: u64 = read(ptr, 64);
let v: FileReadFormat = FileReadFormat::copy_from_raw_ptr(ptr.offset(72));
let ips: Vec<u64> = Vec::new();
let raw_sample: Vec<u8> = Vec::new();
let lbr: Vec<BranchEntry> = Vec::new();
let abi: u64 = 0;
let regs: Vec<u64> = Vec::new();
let user_stack: Vec<u8> = Vec::new();
let dyn_size: u64 = 0;
let weight: u64 = 0;
let data_str: u64 = 0;
SampleRecord {
header: header,
ip: ip,
pid: pid,
tid: tid,
time: time,
addr: addr,
id: id,
stream_id: stream_id,
cpu: cpu,
res: res,
period: period,
v: v,
ips: ips,
raw_sample: raw_sample,
lbr: lbr,
abi: abi,
regs: regs,
user_stack: user_stack,
dyn_size: dyn_size,
weight: weight,
data_str: data_str,
}
}
}
#[derive(Debug)]
pub enum Event {
MMAP(MMAPRecord),
Lost(LostRecord),
Comm(CommRecord),
Exit(ExitRecord),
Throttle(ThrottleRecord),
Unthrottle(ThrottleRecord),
Fork(ForkRecord),
Read(ReadRecord),
Sample(SampleRecord),
}
impl Iterator for SamplingPerfCounter {
type Item = Event;
fn next(&mut self) -> Option<Event> {
if self.header().data_tail < self.header().data_head {
let offset: isize = (self.header().data_tail as usize % self.events_size) as isize;
let mut bytes_read = 0;
let event_ptr = unsafe { self.events().offset(offset) };
let event: EventHeader = unsafe { EventHeader::copy_from_raw_ptr(event_ptr) };
bytes_read += mem::size_of::<EventHeader>() as u64;
let record = match event.event_type {
perf_event::PERF_RECORD_MMAP => {
let record: MMAPRecord = unsafe { MMAPRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::MMAP(record))
}
perf_event::PERF_RECORD_LOST => {
let record: LostRecord = unsafe { LostRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Lost(record))
}
perf_event::PERF_RECORD_COMM => {
let record: CommRecord = unsafe { CommRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Comm(record))
}
perf_event::PERF_RECORD_EXIT => {
let record: ExitRecord = unsafe { ExitRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Exit(record))
}
perf_event::PERF_RECORD_THROTTLE => {
let record: ThrottleRecord =
unsafe { ThrottleRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Throttle(record))
}
perf_event::PERF_RECORD_UNTHROTTLE => {
let record: ThrottleRecord =
unsafe { ThrottleRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Unthrottle(record))
}
perf_event::PERF_RECORD_FORK => {
let record: ForkRecord = unsafe { ForkRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Fork(record))
}
perf_event::PERF_RECORD_READ => {
let record: ReadRecord = unsafe { ReadRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Read(record))
}
perf_event::PERF_RECORD_SAMPLE => {
let record: SampleRecord =
unsafe { SampleRecord::copy_from_raw_ptr(event_ptr) };
Some(Event::Sample(record))
}
perf_event::PERF_RECORD_MMAP2 => {
unreachable!();
}
_ => {
panic!("Unknown type!");
}
};
let header = self.mut_header();
header.data_tail = bytes_read;
record
} else {
None
}
}
}
impl SamplingPerfCounter {
pub fn new(pc: PerfCounter) -> SamplingPerfCounter {
let size = (1 + 16) * 4096;
let res: mmap::MemoryMap = mmap::MemoryMap::new(
size,
&[
mmap::MapOption::MapFd(pc.fd),
mmap::MapOption::MapOffset(0),
mmap::MapOption::MapNonStandardFlags(MAP_SHARED),
mmap::MapOption::MapReadable,
],
)
.unwrap();
SamplingPerfCounter {
pc: pc,
map: res,
events_size: 16 * 4096,
}
}
fn header(&self) -> &MMAPPage {
unsafe { mem::transmute::<*mut u8, &MMAPPage>(self.map.data()) }
}
fn mut_header(&mut self) -> &mut MMAPPage {
unsafe { mem::transmute::<*mut u8, &mut MMAPPage>(self.map.data()) }
}
fn events(&self) -> *const u8 {
unsafe { self.map.data().offset(4096) }
}
pub fn print(&mut self) {
let event: Event = self.next().unwrap();
println!("{:?}", event);
match event {
Event::MMAP(a) => println!("{:?}", a.filename),
Event::Lost(a) => println!("{:?}", a),
Event::Comm(a) => println!("{:?}", a),
Event::Exit(a) => println!("{:?}", a),
Event::Throttle(a) => println!("{:?}", a),
Event::Unthrottle(a) => println!("{:?}", a),
Event::Fork(a) => println!("{:?}", a),
Event::Read(a) => println!("{:?}", a),
Event::Sample(a) => println!("{:?}", a),
}
}
}