Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/tracexec-exporter-perfetto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ serde = { workspace = true }
prost = { workspace = true }
bytes = { workspace = true }
tokio = { workspace = true }
nix = { workspace = true }
nix = { workspace = true, features = ["time"] }

[dev-dependencies]
test-that = { workspace = true }
81 changes: 73 additions & 8 deletions crates/tracexec-exporter-perfetto/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use chrono::{
DateTime,
Local,
};
use nix::time::{
ClockId,
clock_gettime,
};
use tracexec_core::{
cli::args::ModifierArgs,
copy::{
Expand Down Expand Up @@ -36,6 +40,7 @@ use crate::{
},
producer::TrackUuid,
proto::{
BuiltinClock,
ClockSnapshot,
DebugAnnotation,
DebugAnnotationName,
Expand All @@ -46,7 +51,7 @@ use crate::{
TracePacketDefaults,
TrackDescriptor,
TrackEvent,
clock_snapshot::clock::BuiltinClocks,
clock_snapshot::Clock,
debug_annotation,
trace_packet::{
Data,
Expand Down Expand Up @@ -82,15 +87,15 @@ pub struct TracePacketCreator {

impl TracePacketCreator {
/// Create a creator and the initial packet that needs to be sent first
pub fn new(baseline: Arc<BaselineInfo>) -> (Self, TracePacket) {
pub fn new(baseline: Arc<BaselineInfo>) -> color_eyre::Result<(Self, TracePacket)> {
let mut packet = Self::boilerplate();
// sequence id related
packet.sequence_flags = Some(SequenceFlags::SeqIncrementalStateCleared as u32);
packet.previous_packet_dropped = Some(true);
packet.first_packet_on_sequence = Some(true);
packet.optional_trusted_packet_sequence_id = Some(TRUSTED_PKT_SEQ_ID);
packet.trace_packet_defaults = Some(TracePacketDefaults {
timestamp_clock_id: Some(BuiltinClocks::RealtimeCoarse as u32),
timestamp_clock_id: Some(BuiltinClock::Realtime as u32),
..Default::default()
});
packet.interned_data = Some(InternedData {
Expand All @@ -100,12 +105,27 @@ impl TracePacketCreator {
debug_annotation_string_values: vec![],
..Default::default()
});
// Event timestamps are Unix time. Relate that clock to Perfetto's default
// BOOTTIME domain using adjacent readings, rather than assuming alignment.
let boottime = clock_gettime(ClockId::CLOCK_BOOTTIME)?;
let realtime = clock_gettime(ClockId::CLOCK_REALTIME)?;
packet.data = Some(Data::ClockSnapshot(ClockSnapshot {
clocks: vec![],
primary_trace_clock: Some(BuiltinClocks::RealtimeCoarse as i32),
clocks: vec![
Clock {
clock_id: Some(BuiltinClock::Boottime as u32),
timestamp: Some(boottime.tv_sec() as u64 * 1_000_000_000 + boottime.tv_nsec() as u64),
..Default::default()
},
Clock {
clock_id: Some(BuiltinClock::Realtime as u32),
timestamp: Some(realtime.tv_sec() as u64 * 1_000_000_000 + realtime.tv_nsec() as u64),
..Default::default()
},
],
primary_trace_clock: Some(BuiltinClock::Realtime as i32),
}));
#[expect(clippy::unwrap_used)]
(
Ok((
Self {
modifier_args: ModifierArgs::default(),
da_string_interner: ValueInterner::new(NonZeroUsize::new(114_514).unwrap(), 1),
Expand All @@ -117,7 +137,7 @@ impl TracePacketCreator {
baseline,
},
packet,
)
))
}

fn boilerplate() -> TracePacket {
Expand Down Expand Up @@ -468,6 +488,11 @@ mod tests {

use nix::{
errno::Errno,
sys::time::TimeValLike,
time::{
ClockId,
clock_gettime,
},
unistd::Pid,
};
use test_that::prelude::*;
Expand Down Expand Up @@ -497,6 +522,7 @@ mod tests {
intern::DebugAnnotationInternId,
producer::TrackUuid,
proto::{
BuiltinClock,
debug_annotation::{
NameField as DebugNameField,
Value,
Expand All @@ -505,6 +531,45 @@ mod tests {
},
};

#[test]
fn test_initial_packet_relates_boottime_to_event_clock() {
let baseline = Arc::new(BaselineInfo::new().unwrap());
let boottime_before = clock_gettime(ClockId::CLOCK_BOOTTIME).unwrap();
let realtime_before = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();
let (_, initial) = TracePacketCreator::new(baseline).unwrap();
let boottime_after = clock_gettime(ClockId::CLOCK_BOOTTIME).unwrap();
let realtime_after = clock_gettime(ClockId::CLOCK_REALTIME).unwrap();

let event_clock = initial.trace_packet_defaults.unwrap().timestamp_clock_id;
assert_eq!(event_clock, Some(BuiltinClock::Realtime as u32));
let Some(Data::ClockSnapshot(snapshot)) = initial.data else {
panic!("expected initial clock snapshot");
};
assert_eq!(
snapshot.primary_trace_clock,
Some(BuiltinClock::Realtime as i32)
);

// Both clock domains must have real nanosecond readings in the same
// snapshot. Selecting a primary clock alone does not connect them.
for (clock_id, before, after) in [
(BuiltinClock::Boottime, boottime_before, boottime_after),
(BuiltinClock::Realtime, realtime_before, realtime_after),
] {
let clock = snapshot
.clocks
.iter()
.find(|clock| clock.clock_id == Some(clock_id as u32))
.expect("missing clock relationship");
let timestamp = clock.timestamp.expect("missing clock reading");
assert!(
(before.num_nanoseconds() as u64..=after.num_nanoseconds() as u64).contains(&timestamp)
);
assert!(!clock.is_incremental.unwrap_or(false));
assert_eq!(clock.unit_multiplier_ns.unwrap_or(1), 1);
}
}

fn make_exec_event(cgroup: CgroupInfo) -> TracerEventDetails {
TracerEventDetails::Exec(Box::new(ExecEvent {
syscall: ExecSyscall::Execve,
Expand Down Expand Up @@ -548,7 +613,7 @@ mod tests {
#[test]
fn test_process_exec_event_includes_cgroup_debug_annotation() {
let baseline = Arc::new(BaselineInfo::new().unwrap());
let (mut creator, _initial) = TracePacketCreator::new(baseline);
let (mut creator, _initial) = TracePacketCreator::new(baseline).unwrap();
let packet = creator
.process_exec_event(
&make_exec_event(CgroupInfo::V2 {
Expand Down
2 changes: 1 addition & 1 deletion crates/tracexec-exporter-perfetto/src/perfetto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Exporter for PerfettoExporter {

#[allow(clippy::future_not_send)]
async fn run(mut self) -> Result<i32, Self::Error> {
let (mut producer, initial_packet) = TracePacketProducer::new(self.meta.baseline);
let (mut producer, initial_packet) = TracePacketProducer::new(self.meta.baseline)?;
self.recorder.record(initial_packet)?;
while let Some(message) = self.stream.recv().await {
match message {
Expand Down
14 changes: 7 additions & 7 deletions crates/tracexec-exporter-perfetto/src/producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,10 @@ fn allocate_track_id() -> TrackUuid {
}

impl TracePacketProducer {
pub fn new(baseline: Arc<BaselineInfo>) -> (Self, TracePacket) {
pub fn new(baseline: Arc<BaselineInfo>) -> color_eyre::Result<(Self, TracePacket)> {
let uuid = allocate_track_id();
let (creator, packet) = TracePacketCreator::new(baseline);
(
let (creator, packet) = TracePacketCreator::new(baseline)?;
Ok((
Self {
tracks: Track {
uuid,
Expand All @@ -284,7 +284,7 @@ impl TracePacketProducer {
creator,
},
packet,
)
))
}

pub fn ensure_failure_track(&self) -> (TrackUuid, Option<TrackDescriptor>) {
Expand Down Expand Up @@ -585,7 +585,7 @@ mod tests {
#[test]
fn test_trace_packet_producer_exec_success_and_exit() {
let baseline = Arc::new(BaselineInfo::new().unwrap());
let (mut producer, _initial) = TracePacketProducer::new(baseline);
let (mut producer, _initial) = TracePacketProducer::new(baseline).unwrap();
let id = EventId::new(1);
let exec = make_exec_event(1234, "/bin/echo", 0, None);
let exec_event = TracerMessage::Event(TracerEvent {
Expand All @@ -611,7 +611,7 @@ mod tests {
#[test]
fn test_trace_packet_producer_exec_failure_parentless_track_reuse() {
let baseline = Arc::new(BaselineInfo::new().unwrap());
let (mut producer, _initial) = TracePacketProducer::new(baseline);
let (mut producer, _initial) = TracePacketProducer::new(baseline).unwrap();
let exec_fail = make_exec_event(1234, "/bin/echo", -1, None);
let packets = producer
.process(TracerMessage::Event(TracerEvent {
Expand All @@ -634,7 +634,7 @@ mod tests {
#[test]
fn test_trace_packet_producer_spawn_child_allocates_track() {
let baseline = Arc::new(BaselineInfo::new().unwrap());
let (mut producer, _initial) = TracePacketProducer::new(baseline);
let (mut producer, _initial) = TracePacketProducer::new(baseline).unwrap();
let parent_id = EventId::new(1);
let parent = make_exec_event(2000, "/bin/parent", 0, None);
let parent_event = TracerMessage::Event(TracerEvent {
Expand Down