1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
use crate::PerfByteReader;
use core::time;
const BILLION32: u32 = 1_000_000_000;
const BILLION64: u64 = 1_000_000_000;
/// Semantics equivalent to `struct timespec` from `time.h`.
/// Time = 1970 + seconds + nanoseconds.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PerfTimeSpec {
seconds: i64,
nanoseconds: u32,
}
impl PerfTimeSpec {
/// Returns the Unix epoch, 1970-01-01 00:00:00.
pub const UNIX_EPOCH: PerfTimeSpec = PerfTimeSpec {
seconds: 0,
nanoseconds: 0,
};
/// Returns the maximum representable value (year 292,277,026,596).
pub const MAX: PerfTimeSpec = PerfTimeSpec {
seconds: i64::MAX,
nanoseconds: BILLION32 - 1,
};
/// Returns the minimum representable value (year -292,277,022,656, or BC 292,277,022,657).
pub const MIN: PerfTimeSpec = PerfTimeSpec {
seconds: i64::MIN,
nanoseconds: 0,
};
/// Initializes a new instance of the PerfTimeSpec struct with the specified
/// seconds and nanoseconds, relative to 1970.
///
/// Normalizes nanoseconds to the range 0..999,999,999, i.e. if the nanoseconds
/// parameter exceeds 999,999,999 then an appropriate number of seconds (1..4)
/// will be added to seconds. (Note that this may cause seconds to overflow,
/// which is not checked.)
pub const fn new(seconds: i64, nanoseconds: u32) -> PerfTimeSpec {
let mut this = PerfTimeSpec {
seconds,
nanoseconds,
};
while this.nanoseconds >= BILLION32 {
this.seconds += 1; // May overflow.
this.nanoseconds -= BILLION32;
}
return this;
}
/// Initializes a new instance of the PerfTimeSpec struct with the specified
/// duration, which is treated as the amount of time after the Unix epoch (1970).
/// For example, `from_duration_after_epoch(Duration::from_days(10))` would
/// return a PerfTimeSpec representing midnight of 1970-01-11.
///
/// Requires that the duration is less than or equal to i64::MAX seconds.
pub const fn from_duration_after_epoch(duration: time::Duration) -> PerfTimeSpec {
let seconds = duration.as_secs();
assert!(
seconds <= i64::MAX as u64,
"Duration too large for PerfTimeSpec"
);
return PerfTimeSpec::new(seconds as i64, duration.subsec_nanos());
}
/// Returns the number of whole seconds since the Unix epoch (1970-01-01 00:00:00).
pub fn seconds(&self) -> i64 {
return self.seconds;
}
/// Returns the number of nanoseconds since the last whole second, in the range 0..999,999,999.
pub fn nanoseconds(&self) -> u32 {
return self.nanoseconds;
}
/// Returns this time as a `time::Duration` after the Unix epoch (1970).
/// Returns `None` if this time is before the Unix epoch (1970).
pub fn as_duration_after_epoch(&self) -> Option<time::Duration> {
if self.seconds < 0 {
return None;
} else {
return Some(time::Duration::new(self.seconds as u64, self.nanoseconds));
}
}
/// Returns a new PerfTimeSpec that is the sum of this + nanoseconds.
pub fn add_nanoseconds(&self, nanoseconds: u64) -> PerfTimeSpec {
let sec = (nanoseconds / BILLION64) as i64;
let nsec = (nanoseconds % BILLION64) as u32;
// The nsec parameter is in the range (0..+2Billion). The PerfTimeSpec constructor
// will normalize this.
return PerfTimeSpec::new(self.seconds + sec, self.nanoseconds + nsec);
}
}
/// Information about a perf event collection session.
#[derive(Clone, Copy, Debug)]
pub struct PerfSessionInfo {
clock_offset_seconds: i64,
clock_offset_nanoseconds: u32,
clock_id: u32,
clock_offset_known: bool,
byte_reader: PerfByteReader,
}
impl PerfSessionInfo {
/// Constructs a new PerfSessionInfo instance.
/// Instances of this class are normally created and managed by the session
/// manager or file reader.
pub const fn new(byte_reader: PerfByteReader) -> PerfSessionInfo {
PerfSessionInfo {
clock_offset_seconds: 0,
clock_offset_nanoseconds: 0,
clock_id: 0,
clock_offset_known: false,
byte_reader,
}
}
/// Returns true if the the session's event data is formatted in big-endian
/// byte order. (Use `byte_reader` to do byte-swapping as appropriate.)
pub const fn source_big_endian(&self) -> bool {
self.byte_reader.source_big_endian()
}
/// Returns a PerfByteReader configured for the byte order of the events
/// in this session, i.e. `PerfByteReader(this.source_big_endian())`.
pub const fn byte_reader(&self) -> PerfByteReader {
self.byte_reader
}
/// Returns true if session clock offset is known.
pub const fn clock_offset_known(&self) -> bool {
self.clock_offset_known
}
/// Returns the `CLOCK_REALTIME` value that corresponds to an event timestamp of 0
/// for this session. Returns 1970 if the session timestamp offset is unknown.
pub const fn clock_offset(&self) -> PerfTimeSpec {
PerfTimeSpec::new(self.clock_offset_seconds, self.clock_offset_nanoseconds)
}
/// Returns the clockid of the session timestamp, e.g. `CLOCK_MONOTONIC`.
/// Returns `u32::MAX` if the session timestamp clockid is unknown.
pub const fn clock_id(&self) -> u32 {
self.clock_id
}
/// From `HEADER_CLOCKID`. If unknown, use `set_clock_id(u32::MAX)`.
pub fn set_clock_id(&mut self, clock_id: u32) {
self.clock_id = clock_id;
}
/// From HEADER_CLOCK_DATA. If unknown, use SetClockData(u32::MAX, 0, 0).
pub fn set_clock_data(&mut self, clock_id: u32, wall_clock_ns: u64, clock_id_time_ns: u64) {
if clock_id == u32::MAX {
// Offset is unspecified.
self.clock_offset_seconds = 0;
self.clock_offset_nanoseconds = 0;
self.clock_id = clock_id;
self.clock_offset_known = false;
} else if clock_id_time_ns <= wall_clock_ns {
// Offset is positive.
// wallClockNS = clockidTimeNS + offsetNS
// offsetNS = wallClockNS - clockidTimeNS
let offset_ns = wall_clock_ns - clock_id_time_ns;
// offsetNS = sec * Billion + nsec
// sec = offsetNS / Billion
self.clock_offset_seconds = (offset_ns / BILLION64) as i64;
// nsec = offsetNS % Billion
self.clock_offset_nanoseconds = (offset_ns % BILLION64) as u32;
self.clock_id = clock_id;
self.clock_offset_known = true;
} else {
// Offset is negative.
// wallClockNS = clockidTimeNS + offsetNS
// offsetNS = wallClockNS - clockidTimeNS
// -negOffsetNS = wallClockNS - clockidTimeNS
// negOffsetNS = clockidTimeNS - wallClockNS
let neg_offset_ns = clock_id_time_ns - wall_clock_ns;
// negOffsetNS = (negOffsetNS / Billion) * Billion + (negOffsetNS % Billion)
// negOffsetNS = (negOffsetNS / Billion) * Billion + (negOffsetNS % Billion) - Billion + Billion
// negOffsetNS = (negOffsetNS / Billion + 1) * Billion + (negOffsetNS % Billion) - Billion
// negOffsetNS = negSec * Billion + negNsec
// negSec = negOffsetNS / Billion + 1
// negNsec = (negOffsetNS % Billion) - Billion
// sec = -(negOffsetNS / Billion + 1)
self.clock_offset_seconds = -((neg_offset_ns / BILLION64) as i64) - 1;
// nsec = -((negOffsetNS % Billion) - Billion)
self.clock_offset_nanoseconds = BILLION32 - (neg_offset_ns % BILLION64) as u32;
// Fix up case where nsec is too large.
if self.clock_offset_nanoseconds == BILLION32 {
self.clock_offset_seconds += 1;
self.clock_offset_nanoseconds -= BILLION32;
}
self.clock_id = clock_id;
self.clock_offset_known = true;
}
}
/// Gets offset values `(wall_clock_ns, clockid_time_ns)` suitable for use in
/// `HEADER_CLOCK_DATA`.
///
/// Note: The returned NS values may be normalized relative to the values provided
/// to SetClockData, but the difference between them will be the same as the
/// difference between the values provided to SetClockData.
pub const fn get_clock_data(&self) -> (u64, u64) {
if self.clock_offset_seconds >= 0 {
return (
self.clock_offset_seconds as u64 * BILLION64 + self.clock_offset_nanoseconds as u64,
0,
);
} else {
return (
0,
(-self.clock_offset_seconds) as u64 * BILLION64
- self.clock_offset_nanoseconds as u64,
);
}
}
/// Converts time from session timestamp to real-time (time since 1970):
/// `time_to_time_spec = clock_offset() + time`.
///
/// If session clock offset is unknown, assumes 1970.
pub const fn time_to_time_spec(&self, time: u64) -> PerfTimeSpec {
let mut sec = (time / BILLION64) as i64;
let mut nsec = (time % BILLION64) as u32;
sec += self.clock_offset_seconds;
nsec += self.clock_offset_nanoseconds;
if nsec >= BILLION32 {
sec += 1;
nsec -= BILLION32;
}
return PerfTimeSpec::new(sec, nsec);
}
}