Skip to content

Commit 560162a

Browse files
authored
Merge pull request #101 from michaelwoerister/events-with-args
Add support of having arguments on events.
2 parents 2c1ed0d + 9daf790 commit 560162a

File tree

9 files changed

+303
-56
lines changed

9 files changed

+303
-56
lines changed

analyzeme/src/event.rs

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::timestamp::Timestamp;
2+
use memchr::memchr;
23
use std::borrow::Cow;
34
use std::time::Duration;
45

56
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
67
pub struct Event<'a> {
78
pub event_kind: Cow<'a, str>,
89
pub label: Cow<'a, str>,
9-
pub additional_data: &'a [Cow<'a, str>],
10+
pub additional_data: Vec<Cow<'a, str>>,
1011
pub timestamp: Timestamp,
1112
pub thread_id: u32,
1213
}
@@ -36,4 +37,141 @@ impl<'a> Event<'a> {
3637
Timestamp::Instant(_) => None,
3738
}
3839
}
40+
41+
pub(crate) fn parse_event_id(event_id: Cow<'a, str>) -> (Cow<'a, str>, Vec<Cow<'a, str>>) {
42+
let event_id = match event_id {
43+
Cow::Owned(s) => Cow::Owned(s.into_bytes()),
44+
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
45+
};
46+
47+
let mut parser = Parser::new(event_id);
48+
49+
let label = match parser.parse_label() {
50+
Ok(label) => label,
51+
Err(message) => {
52+
eprintln!("{}", message);
53+
return (Cow::from("<parse error>"), Vec::new());
54+
}
55+
};
56+
57+
let mut args = Vec::new();
58+
59+
while parser.pos != parser.full_text.len() {
60+
match parser.parse_arg() {
61+
Ok(arg) => args.push(arg),
62+
Err(message) => {
63+
eprintln!("{}", message);
64+
break;
65+
}
66+
}
67+
}
68+
69+
(label, args)
70+
}
71+
}
72+
73+
struct Parser<'a> {
74+
full_text: Cow<'a, [u8]>,
75+
pos: usize,
76+
}
77+
78+
const SEPARATOR_BYTE: u8 = measureme::event_id::SEPARATOR_BYTE.as_bytes()[0];
79+
80+
impl<'a> Parser<'a> {
81+
fn new(full_text: Cow<'a, [u8]>) -> Parser<'a> {
82+
Parser { full_text, pos: 0 }
83+
}
84+
85+
fn peek(&self) -> u8 {
86+
self.full_text[self.pos]
87+
}
88+
89+
fn parse_label(&mut self) -> Result<Cow<'a, str>, String> {
90+
assert!(self.pos == 0);
91+
self.parse_separator_terminated_text()
92+
}
93+
94+
fn parse_separator_terminated_text(&mut self) -> Result<Cow<'a, str>, String> {
95+
let start = self.pos;
96+
97+
let end = memchr(SEPARATOR_BYTE, &self.full_text[start..])
98+
.map(|pos| pos + start)
99+
.unwrap_or(self.full_text.len());
100+
101+
if start == end {
102+
return self.err("Zero-length <text>");
103+
}
104+
105+
self.pos = end;
106+
107+
if self.full_text[start..end].iter().any(u8::is_ascii_control) {
108+
return self.err("Found ASCII control character in <text>");
109+
}
110+
111+
Ok(self.substring(start, end))
112+
}
113+
114+
fn parse_arg(&mut self) -> Result<Cow<'a, str>, String> {
115+
if self.peek() != SEPARATOR_BYTE {
116+
return self.err(&format!(
117+
"Expected '\\x{:x}' char at start of <argument>",
118+
SEPARATOR_BYTE
119+
));
120+
}
121+
122+
self.pos += 1;
123+
self.parse_separator_terminated_text()
124+
}
125+
126+
fn err<T>(&self, message: &str) -> Result<T, String> {
127+
Err(format!(
128+
r#"Could not parse `event_id`. {} at {} in "{}""#,
129+
message,
130+
self.pos,
131+
std::str::from_utf8(&self.full_text[..]).unwrap()
132+
))
133+
}
134+
135+
fn substring(&self, start: usize, end: usize) -> Cow<'a, str> {
136+
match self.full_text {
137+
Cow::Owned(ref s) => {
138+
let bytes = s[start..end].to_owned();
139+
Cow::Owned(String::from_utf8(bytes).unwrap())
140+
}
141+
Cow::Borrowed(s) => Cow::Borrowed(std::str::from_utf8(&s[start..end]).unwrap()),
142+
}
143+
}
144+
}
145+
146+
#[cfg(test)]
147+
mod tests {
148+
use super::*;
149+
use std::borrow::Cow;
150+
151+
#[test]
152+
fn parse_event_id_no_args() {
153+
let (label, args) = Event::parse_event_id(Cow::from("foo"));
154+
155+
assert_eq!(label, "foo");
156+
assert!(args.is_empty());
157+
}
158+
159+
#[test]
160+
fn parse_event_id_one_arg() {
161+
let (label, args) = Event::parse_event_id(Cow::from("foo\x1emy_arg"));
162+
163+
assert_eq!(label, "foo");
164+
assert_eq!(args, vec![Cow::from("my_arg")]);
165+
}
166+
167+
#[test]
168+
fn parse_event_id_n_args() {
169+
let (label, args) = Event::parse_event_id(Cow::from("foo\x1earg1\x1earg2\x1earg3"));
170+
171+
assert_eq!(label, "foo");
172+
assert_eq!(
173+
args,
174+
vec![Cow::from("arg1"), Cow::from("arg2"), Cow::from("arg3")]
175+
);
176+
}
39177
}

analyzeme/src/profiling_data.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::event::Event;
22
use crate::lightweight_event::LightweightEvent;
3-
use crate::StringTable;
43
use crate::timestamp::Timestamp;
4+
use crate::StringTable;
55
use measureme::file_header::{
66
read_file_header, write_file_header, CURRENT_FILE_FORMAT_VERSION, FILE_HEADER_SIZE,
77
FILE_MAGIC_EVENT_STREAM,
88
};
99
use measureme::ByteVecSink;
10-
use measureme::{ProfilerFiles, RawEvent, SerializationSink, StringTableBuilder};
10+
use measureme::{EventId, ProfilerFiles, RawEvent, SerializationSink, StringTableBuilder};
1111
use serde::{Deserialize, Deserializer};
1212
use std::error::Error;
1313
use std::fs;
@@ -94,10 +94,16 @@ impl ProfilingData {
9494

9595
let timestamp = Timestamp::from_raw_event(&raw_event, self.metadata.start_time);
9696

97+
let event_id = string_table
98+
.get(raw_event.event_id.to_string_id())
99+
.to_string();
100+
// Parse out the label and arguments from the `event_id`.
101+
let (label, additional_data) = Event::parse_event_id(event_id);
102+
97103
Event {
98104
event_kind: string_table.get(raw_event.event_kind).to_string(),
99-
label: string_table.get(raw_event.event_id).to_string(),
100-
additional_data: &[],
105+
label,
106+
additional_data,
101107
timestamp,
102108
thread_id: raw_event.thread_id,
103109
}
@@ -226,7 +232,7 @@ impl ProfilingDataBuilder {
226232
F: FnOnce(&mut Self),
227233
{
228234
let event_kind = self.string_table.alloc(event_kind);
229-
let event_id = self.string_table.alloc(event_id);
235+
let event_id = EventId::from_label(self.string_table.alloc(event_id));
230236

231237
inner(self);
232238

@@ -247,7 +253,7 @@ impl ProfilingDataBuilder {
247253
timestamp_nanos: u64,
248254
) -> &mut Self {
249255
let event_kind = self.string_table.alloc(event_kind);
250-
let event_id = self.string_table.alloc(event_id);
256+
let event_id = EventId::from_label(self.string_table.alloc(event_id));
251257

252258
let raw_event = RawEvent::new_instant(event_kind, event_id, thread_id, timestamp_nanos);
253259

@@ -319,7 +325,7 @@ mod tests {
319325
Event {
320326
event_kind: Cow::from(event_kind),
321327
label: Cow::from(label),
322-
additional_data: &[],
328+
additional_data: Vec::new(),
323329
timestamp: Timestamp::Interval {
324330
start: SystemTime::UNIX_EPOCH + Duration::from_nanos(start_nanos),
325331
end: SystemTime::UNIX_EPOCH + Duration::from_nanos(end_nanos),
@@ -337,7 +343,7 @@ mod tests {
337343
Event {
338344
event_kind: Cow::from(event_kind),
339345
label: Cow::from(label),
340-
additional_data: &[],
346+
additional_data: Vec::new(),
341347
timestamp: Timestamp::Instant(
342348
SystemTime::UNIX_EPOCH + Duration::from_nanos(timestamp_nanos),
343349
),
@@ -399,7 +405,6 @@ mod tests {
399405
assert_eq!(events[0].to_event(), full_interval("k1", "id1", 0, 10, 100));
400406
assert_eq!(events[1].to_event(), full_interval("k2", "id2", 1, 100, 110));
401407
assert_eq!(events[2].to_event(), full_interval("k3", "id3", 0, 120, 140));
402-
403408
}
404409

405410
#[test]

analyzeme/src/stringtable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use measureme::file_header::{
77
};
88
use measureme::stringtable::{METADATA_STRING_ID, STRING_ID_MASK, TERMINATOR};
99
use measureme::{Addr, StringId};
10+
use memchr::memchr;
1011
use rustc_hash::FxHashMap;
1112
use std::borrow::Cow;
1213
use std::error::Error;
13-
use memchr::memchr;
1414

1515
fn deserialize_index_entry(bytes: &[u8]) -> (StringId, Addr) {
1616
(

analyzeme/src/testing_common.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::timestamp::Timestamp;
22
use crate::{Event, ProfilingData};
3-
use measureme::{Profiler, SerializationSink, StringId};
3+
use measureme::{EventId, EventIdBuilder, Profiler, SerializationSink, StringId};
44
use rustc_hash::FxHashMap;
55
use std::borrow::Cow;
66
use std::default::Default;
@@ -18,6 +18,23 @@ fn mk_filestem(file_name_stem: &str) -> PathBuf {
1818
path
1919
}
2020

21+
#[derive(Clone)]
22+
struct ExpectedEvent {
23+
kind: Cow<'static, str>,
24+
label: Cow<'static, str>,
25+
args: Vec<Cow<'static, str>>,
26+
}
27+
28+
impl ExpectedEvent {
29+
fn new(kind: &'static str, label: &'static str, args: &[&'static str]) -> ExpectedEvent {
30+
ExpectedEvent {
31+
kind: Cow::from(kind),
32+
label: Cow::from(label),
33+
args: args.iter().map(|&x| Cow::from(x)).collect(),
34+
}
35+
}
36+
}
37+
2138
// Generate some profiling data. This is the part that would run in rustc.
2239
fn generate_profiling_data<S: SerializationSink>(
2340
filestem: &Path,
@@ -26,27 +43,35 @@ fn generate_profiling_data<S: SerializationSink>(
2643
) -> Vec<Event<'static>> {
2744
let profiler = Arc::new(Profiler::<S>::new(Path::new(filestem)).unwrap());
2845

29-
let event_id_virtual = StringId::new_virtual(42);
46+
let event_id_virtual = EventId::from_label(StringId::new_virtual(42));
47+
let event_id_builder = EventIdBuilder::new(&profiler);
3048

31-
let event_ids = vec![
49+
let event_ids: Vec<(StringId, EventId)> = vec![
3250
(
3351
profiler.alloc_string("Generic"),
34-
profiler.alloc_string("SomeGenericActivity"),
52+
EventId::from_label(profiler.alloc_string("SomeGenericActivity")),
3553
),
3654
(profiler.alloc_string("Query"), event_id_virtual),
55+
(
56+
profiler.alloc_string("QueryWithArg"),
57+
event_id_builder.from_label_and_arg(
58+
profiler.alloc_string("AQueryWithArg"),
59+
profiler.alloc_string("some_arg"),
60+
),
61+
),
3762
];
3863

3964
// This and event_ids have to match!
40-
let mut event_ids_as_str: FxHashMap<_, _> = Default::default();
41-
event_ids_as_str.insert(event_ids[0].0, "Generic");
42-
event_ids_as_str.insert(event_ids[0].1, "SomeGenericActivity");
43-
event_ids_as_str.insert(event_ids[1].0, "Query");
44-
event_ids_as_str.insert(event_ids[1].1, "SomeQuery");
65+
let expected_events_templates = vec![
66+
ExpectedEvent::new("Generic", "SomeGenericActivity", &[]),
67+
ExpectedEvent::new("Query", "SomeQuery", &[]),
68+
ExpectedEvent::new("QueryWithArg", "AQueryWithArg", &["some_arg"]),
69+
];
4570

4671
let threads: Vec<_> = (0.. num_threads).map(|thread_id| {
4772
let event_ids = event_ids.clone();
4873
let profiler = profiler.clone();
49-
let event_ids_as_str = event_ids_as_str.clone();
74+
let expected_events_templates = expected_events_templates.clone();
5075

5176
std::thread::spawn(move || {
5277
let mut expected_events = Vec::new();
@@ -60,7 +85,7 @@ fn generate_profiling_data<S: SerializationSink>(
6085
thread_id as u32,
6186
4,
6287
&event_ids[..],
63-
&event_ids_as_str,
88+
&expected_events_templates,
6489
&mut expected_events,
6590
);
6691
}
@@ -74,7 +99,7 @@ fn generate_profiling_data<S: SerializationSink>(
7499
// An example of allocating the string contents of an event id that has
75100
// already been used
76101
profiler.map_virtual_to_concrete_string(
77-
event_id_virtual,
102+
event_id_virtual.to_string_id(),
78103
profiler.alloc_string("SomeQuery")
79104
);
80105

@@ -165,15 +190,17 @@ fn pseudo_invocation<S: SerializationSink>(
165190
random: usize,
166191
thread_id: u32,
167192
recursions_left: usize,
168-
event_ids: &[(StringId, StringId)],
169-
event_ids_as_str: &FxHashMap<StringId, &'static str>,
193+
event_ids: &[(StringId, EventId)],
194+
expected_events_templates: &[ExpectedEvent],
170195
expected_events: &mut Vec<Event<'static>>,
171196
) {
172197
if recursions_left == 0 {
173198
return;
174199
}
175200

176-
let (event_kind, event_id) = event_ids[random % event_ids.len()];
201+
let random_event_index = random % event_ids.len();
202+
203+
let (event_kind, event_id) = event_ids[random_event_index];
177204

178205
let _prof_guard = profiler.start_recording_interval_event(event_kind, event_id, thread_id);
179206

@@ -183,19 +210,19 @@ fn pseudo_invocation<S: SerializationSink>(
183210
thread_id,
184211
recursions_left - 1,
185212
event_ids,
186-
event_ids_as_str,
213+
expected_events_templates,
187214
expected_events,
188215
);
189216

190217
expected_events.push(Event {
191-
event_kind: Cow::from(event_ids_as_str[&event_kind]),
192-
label: Cow::from(event_ids_as_str[&event_id]),
193-
additional_data: &[],
218+
event_kind: expected_events_templates[random_event_index].kind.clone(),
219+
label: expected_events_templates[random_event_index].label.clone(),
220+
additional_data: expected_events_templates[random_event_index].args.clone(),
221+
thread_id,
194222
// We can't test this anyway:
195223
timestamp: Timestamp::Interval {
196224
start: SystemTime::UNIX_EPOCH,
197225
end: SystemTime::UNIX_EPOCH,
198226
},
199-
thread_id,
200227
});
201228
}

0 commit comments

Comments
 (0)