|
|
|
@ -145,28 +145,24 @@ impl PerfBuffer {
|
|
|
|
|
return Err(PerfBufferError::NoBuffers);
|
|
|
|
|
}
|
|
|
|
|
let header = self.buf().as_ptr();
|
|
|
|
|
let base = header as usize + self.page_size;
|
|
|
|
|
let base = unsafe { header.byte_add(self.page_size) };
|
|
|
|
|
|
|
|
|
|
let mut events = Events { read: 0, lost: 0 };
|
|
|
|
|
let mut buf_n = 0;
|
|
|
|
|
|
|
|
|
|
let fill_buf = |start_off, base, mmap_size, out_buf: &mut [u8]| {
|
|
|
|
|
let fill_buf = |start_off, base: *const u8, mmap_size, out_buf: &mut [u8]| {
|
|
|
|
|
let len = out_buf.len();
|
|
|
|
|
|
|
|
|
|
let end = (start_off + len) % mmap_size;
|
|
|
|
|
let start = start_off % mmap_size;
|
|
|
|
|
|
|
|
|
|
if start < end {
|
|
|
|
|
out_buf.copy_from_slice(unsafe {
|
|
|
|
|
slice::from_raw_parts((base + start) as *const u8, len)
|
|
|
|
|
});
|
|
|
|
|
out_buf.copy_from_slice(unsafe { slice::from_raw_parts(base.add(start), len) });
|
|
|
|
|
} else {
|
|
|
|
|
let size = mmap_size - start;
|
|
|
|
|
unsafe {
|
|
|
|
|
out_buf[..size]
|
|
|
|
|
.copy_from_slice(slice::from_raw_parts((base + start) as *const u8, size));
|
|
|
|
|
out_buf[size..]
|
|
|
|
|
.copy_from_slice(slice::from_raw_parts(base as *const u8, len - size));
|
|
|
|
|
out_buf[..size].copy_from_slice(slice::from_raw_parts(base.add(start), size));
|
|
|
|
|
out_buf[size..].copy_from_slice(slice::from_raw_parts(base, len - size));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
@ -227,11 +223,11 @@ impl PerfBuffer {
|
|
|
|
|
let buf = &mut buffers[buf_n];
|
|
|
|
|
|
|
|
|
|
let event_start = tail % self.size;
|
|
|
|
|
let event =
|
|
|
|
|
unsafe { ptr::read_unaligned((base + event_start) as *const perf_event_header) };
|
|
|
|
|
let event: perf_event_header =
|
|
|
|
|
unsafe { ptr::read_unaligned(base.byte_add(event_start).cast()) };
|
|
|
|
|
let event_size = event.size as usize;
|
|
|
|
|
|
|
|
|
|
match read_event(event_start, event.type_, base, buf) {
|
|
|
|
|
match read_event(event_start, event.type_, base.cast(), buf) {
|
|
|
|
|
Ok(Some((read, lost))) => {
|
|
|
|
|
if read > 0 {
|
|
|
|
|
buf_n += 1;
|
|
|
|
@ -290,14 +286,15 @@ mod tests {
|
|
|
|
|
data: [u8; PAGE_SIZE * 2],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fake_mmap(buf: &MMappedBuf) {
|
|
|
|
|
fn fake_mmap(buf: &mut MMappedBuf) {
|
|
|
|
|
let buf: *mut _ = buf;
|
|
|
|
|
override_syscall(|call| match call {
|
|
|
|
|
Syscall::PerfEventOpen { .. } | Syscall::PerfEventIoctl { .. } => {
|
|
|
|
|
Ok(crate::MockableFd::mock_signed_fd().into())
|
|
|
|
|
}
|
|
|
|
|
call => panic!("unexpected syscall: {:?}", call),
|
|
|
|
|
});
|
|
|
|
|
TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf as *const _ as *mut _);
|
|
|
|
|
TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf.cast());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
@ -318,27 +315,25 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_no_out_bufs() {
|
|
|
|
|
let mmapped_buf = MMappedBuf {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
assert_matches!(buf.read_events(&mut []), Err(PerfBufferError::NoBuffers))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
miri,
|
|
|
|
|
ignore = "`unsafe { (*header).data_tail = tail as u64 };` is attempting a write access using using a tag that only grants SharedReadOnly permission"
|
|
|
|
|
)]
|
|
|
|
|
fn test_no_events() {
|
|
|
|
|
let mmapped_buf = MMappedBuf {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let out_buf = BytesMut::with_capacity(4);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
buf.read_events(&mut [out_buf]).unwrap(),
|
|
|
|
@ -346,13 +341,18 @@ mod tests {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write<T: Debug>(mmapped_buf: &mut MMappedBuf, offset: usize, value: T) -> usize {
|
|
|
|
|
let dst: *mut _ = mmapped_buf;
|
|
|
|
|
let head = offset + mem::size_of::<T>();
|
|
|
|
|
unsafe {
|
|
|
|
|
ptr::write_unaligned(dst.byte_add(PAGE_SIZE + offset).cast(), value);
|
|
|
|
|
mmapped_buf.mmap_page.data_head = head as u64;
|
|
|
|
|
}
|
|
|
|
|
head
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_read_first_lost() {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct LostSamples {
|
|
|
|
@ -361,7 +361,14 @@ mod tests {
|
|
|
|
|
count: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let evt = LostSamples {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
write(
|
|
|
|
|
&mut mmapped_buf,
|
|
|
|
|
0,
|
|
|
|
|
LostSamples {
|
|
|
|
|
header: perf_event_header {
|
|
|
|
|
type_: PERF_RECORD_LOST as u32,
|
|
|
|
|
misc: 0,
|
|
|
|
@ -369,10 +376,12 @@ mod tests {
|
|
|
|
|
},
|
|
|
|
|
id: 1,
|
|
|
|
|
count: 0xCAFEBABE,
|
|
|
|
|
};
|
|
|
|
|
write(&mut mmapped_buf, 0, evt);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let out_buf = BytesMut::with_capacity(0);
|
|
|
|
|
let events = buf.read_events(&mut [out_buf]).unwrap();
|
|
|
|
|
assert_eq!(events.lost, 0xCAFEBABE);
|
|
|
|
@ -385,17 +394,11 @@ mod tests {
|
|
|
|
|
value: T,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write<T: Debug>(mmapped_buf: &mut MMappedBuf, offset: usize, value: T) -> usize {
|
|
|
|
|
let dst: *mut _ = mmapped_buf;
|
|
|
|
|
unsafe {
|
|
|
|
|
ptr::write_unaligned(dst.byte_add(PAGE_SIZE + offset).cast(), value);
|
|
|
|
|
mmapped_buf.mmap_page.data_head = (offset + mem::size_of::<T>()) as u64;
|
|
|
|
|
mmapped_buf.mmap_page.data_head as usize
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write_sample<T: Debug>(mmapped_buf: &mut MMappedBuf, offset: usize, value: T) -> usize {
|
|
|
|
|
let sample = PerfSample {
|
|
|
|
|
write(
|
|
|
|
|
mmapped_buf,
|
|
|
|
|
offset,
|
|
|
|
|
PerfSample {
|
|
|
|
|
s_hdr: Sample {
|
|
|
|
|
header: perf_event_header {
|
|
|
|
|
type_: PERF_RECORD_SAMPLE as u32,
|
|
|
|
@ -405,8 +408,8 @@ mod tests {
|
|
|
|
|
size: mem::size_of::<T>() as u32,
|
|
|
|
|
},
|
|
|
|
|
value,
|
|
|
|
|
};
|
|
|
|
|
write(mmapped_buf, offset, sample)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn u32_from_buf(buf: &[u8]) -> u32 {
|
|
|
|
@ -422,11 +425,12 @@ mod tests {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
write_sample(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut out_bufs = [BytesMut::with_capacity(4)];
|
|
|
|
|
|
|
|
|
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
|
|
|
@ -439,12 +443,13 @@ mod tests {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let next = write_sample(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
|
|
|
|
write_sample(&mut mmapped_buf, next, 0xBADCAFEu32);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut out_bufs = [BytesMut::with_capacity(4)];
|
|
|
|
|
|
|
|
|
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
|
|
|
@ -461,12 +466,13 @@ mod tests {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let next = write_sample(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
|
|
|
|
write_sample(&mut mmapped_buf, next, 0xBADCAFEu32);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut out_bufs = (0..3)
|
|
|
|
|
.map(|_| BytesMut::with_capacity(4))
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
@ -482,12 +488,13 @@ mod tests {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let offset = PAGE_SIZE - mem::size_of::<PerfSample<u32>>();
|
|
|
|
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
|
|
|
|
write_sample(&mut mmapped_buf, offset, 0xCAFEBABEu32);
|
|
|
|
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut out_bufs = [BytesMut::with_capacity(4)];
|
|
|
|
|
|
|
|
|
@ -501,31 +508,31 @@ mod tests {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let header = perf_event_header {
|
|
|
|
|
let offset = PAGE_SIZE - mem::size_of::<perf_event_header>() - 2;
|
|
|
|
|
write(
|
|
|
|
|
&mut mmapped_buf,
|
|
|
|
|
offset,
|
|
|
|
|
perf_event_header {
|
|
|
|
|
type_: PERF_RECORD_SAMPLE as u32,
|
|
|
|
|
misc: 0,
|
|
|
|
|
size: mem::size_of::<PerfSample<u64>>() as u16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let offset = PAGE_SIZE - mem::size_of::<perf_event_header>() - 2;
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
|
|
|
|
write(&mut mmapped_buf, offset, header);
|
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
|
{
|
|
|
|
|
write(&mut mmapped_buf, PAGE_SIZE - 2, 0x0004u16);
|
|
|
|
|
write(&mut mmapped_buf, 0, 0x0000u16);
|
|
|
|
|
}
|
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
|
{
|
|
|
|
|
write(&mut mmapped_buf, PAGE_SIZE - 2, 0x0000u16);
|
|
|
|
|
write(&mut mmapped_buf, 0, 0x0004u16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (left, right) = if cfg!(target_endian = "little") {
|
|
|
|
|
(0x0004u16, 0x0000u16)
|
|
|
|
|
} else {
|
|
|
|
|
(0x0000u16, 0x0004u16)
|
|
|
|
|
};
|
|
|
|
|
write(&mut mmapped_buf, PAGE_SIZE - 2, left);
|
|
|
|
|
write(&mut mmapped_buf, 0, right);
|
|
|
|
|
write(&mut mmapped_buf, 2, 0xBAADCAFEu32);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut out_bufs = [BytesMut::with_capacity(8)];
|
|
|
|
|
|
|
|
|
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
|
|
|
@ -538,10 +545,18 @@ mod tests {
|
|
|
|
|
let mut mmapped_buf = MMappedBuf {
|
|
|
|
|
data: [0; PAGE_SIZE * 2],
|
|
|
|
|
};
|
|
|
|
|
fake_mmap(&mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let sample = PerfSample {
|
|
|
|
|
let (left, right) = if cfg!(target_endian = "little") {
|
|
|
|
|
(0xCAFEBABEu32, 0xBAADCAFEu32)
|
|
|
|
|
} else {
|
|
|
|
|
(0xBAADCAFEu32, 0xCAFEBABEu32)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let offset = PAGE_SIZE - mem::size_of::<PerfSample<u32>>();
|
|
|
|
|
write(
|
|
|
|
|
&mut mmapped_buf,
|
|
|
|
|
offset,
|
|
|
|
|
PerfSample {
|
|
|
|
|
s_hdr: Sample {
|
|
|
|
|
header: perf_event_header {
|
|
|
|
|
type_: PERF_RECORD_SAMPLE as u32,
|
|
|
|
@ -550,19 +565,14 @@ mod tests {
|
|
|
|
|
},
|
|
|
|
|
size: mem::size_of::<u64>() as u32,
|
|
|
|
|
},
|
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
|
value: 0xCAFEBABEu32,
|
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
|
value: 0xBAADCAFEu32,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let offset = PAGE_SIZE - mem::size_of::<PerfSample<u32>>();
|
|
|
|
|
value: left,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
write(&mut mmapped_buf, 0, right);
|
|
|
|
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
|
|
|
|
write(&mut mmapped_buf, offset, sample);
|
|
|
|
|
#[cfg(target_endian = "little")]
|
|
|
|
|
write(&mut mmapped_buf, 0, 0xBAADCAFEu32);
|
|
|
|
|
#[cfg(target_endian = "big")]
|
|
|
|
|
write(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
|
|
|
|
|
|
|
|
|
fake_mmap(&mut mmapped_buf);
|
|
|
|
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut out_bufs = [BytesMut::with_capacity(8)];
|
|
|
|
|
|
|
|
|
|