hawktracer_converter_lib/
label_mapping.rs1use hawktracer_parser::{CoreEventKlassId, Event, Value};
2use std::io::BufRead;
3
4#[derive(Default)]
7pub struct LabelMap {
8 mapping: std::collections::HashMap<u64, String>,
9}
10
11impl LabelMap {
12 pub fn new() -> LabelMap {
13 LabelMap {
14 mapping: std::collections::HashMap::<u64, String>::new(),
15 }
16 }
17
18 pub fn load_from_file(&mut self, path: &str) -> std::io::Result<()> {
19 let f = std::fs::File::open(path)?;
20 let file = std::io::BufReader::new(&f);
21 for (i, line) in file.lines().enumerate() {
22 let line = line?;
23 let data: Vec<&str> = line.split(' ').collect();
24 if data.len() != 3 {
25 eprintln!(
26 "invalid mapping in line {}. Expected 3 arguments, was {}",
27 i,
28 data.len()
29 );
30 continue;
31 }
32
33 let id = match data[2].parse::<u64>() {
34 Err(e) => {
35 eprintln!(
36 "Can not parse identifier '{}' in line {}. Error message: {}",
37 data[2], i, e
38 );
39 continue;
40 }
41 Ok(id) => id,
42 };
43
44 let label = data[1].to_string();
45 self.mapping.insert(id, label); }
47
48 Ok(())
49 }
50
51 pub fn get_label(&mut self, id: u64) -> &String {
52 self.mapping.entry(id).or_insert_with(|| {
53 eprintln!("Label for ID {} does not exist in the mapping", id);
54 id.to_string()
55 })
56 }
57
58 pub fn add_mapping(&mut self, id: u64, label: &str) {
59 self.mapping.insert(id, label.to_owned());
60 }
61}
62
63pub struct LabelGetter {
64 label_map: LabelMap,
65 label_fields: std::vec::Vec<String>,
66 mapping_event_id: Option<u32>,
67}
68
69impl LabelGetter {
70 pub fn new(label_map: LabelMap, label_fields: std::vec::Vec<String>) -> LabelGetter {
71 LabelGetter {
72 label_map,
73 label_fields,
74 mapping_event_id: None,
75 }
76 }
77
78 fn update_mapping_event_info(&mut self, event: &Event) -> bool {
80 if self.mapping_event_id.is_none()
81 && event.get_klass_id() == CoreEventKlassId::KlassInfo as u32
82 {
83 let klass_name = event.get_value_string("event_klass_name");
84 if klass_name.is_ok() && klass_name.unwrap() == "HT_StringMappingEvent" {
85 self.mapping_event_id = event.get_value_u32("info_klass_id").ok();
87 }
88 } else if self.mapping_event_id.is_some()
89 && event.get_klass_id() == self.mapping_event_id.unwrap()
90 {
91 if let Ok(label) = event.get_value_string("label") {
92 if let Ok(id) = event.get_value_u64("identifier") {
93 self.label_map.add_mapping(id, label);
94 return true;
95 }
96 }
97 }
98 false
99 }
100
101 pub fn get_label<'a>(&'a mut self, event: &'a Event) -> Option<(&'a String, &'a String)> {
102 if self.update_mapping_event_info(event) {
103 return None
104 }
105
106 for label_field in &self.label_fields {
107 if let Some(value) = event.get_raw_value(label_field) {
108 match value {
109 Value::U64(value) => {
110 return Some((label_field, self.label_map.get_label(*value)));
111 }
112 Value::Str(value) => return Some((label_field, value)),
113 _ => (),
114 }
115 }
116 }
117 None
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn load_from_file_valid_file_should_load_mapping() {
127 let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
128 path.push("resources/test/label_map_valid.txt");
129 let mut map = LabelMap::new();
130
131 map.load_from_file(&path.to_str().unwrap()).unwrap();
132
133 assert_eq!(map.get_label(1), "label1");
134 assert_eq!(map.get_label(2), "label2");
135 assert_eq!(map.get_label(3), "label3");
136 }
137
138 #[test]
139 fn load_from_file_corrupted_file_should_ignore_invalid_values() {
140 let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
141 path.push("resources/test/label_map_invalid.txt");
142 let mut map = LabelMap::new();
143
144 map.load_from_file(&path.to_str().unwrap()).unwrap();
145
146 assert_eq!(map.get_label(8), "valid_label");
147 }
148
149 #[test]
150 fn load_from_file_should_fail_if_file_does_not_exist() {
151 let mut map = LabelMap::new();
152
153 assert!(map.load_from_file("not_existing_file").is_err());
154 }
155
156 #[test]
157 fn map_should_return_number_if_mapping_does_not_exist() {
158 let mut map = LabelMap::new();
159
160 assert_eq!(map.get_label(4), "4");
161 }
162
163 #[test]
164 fn map_should_return_label_if_mapping_exist() {
165 let mut map = LabelMap::new();
166 let label = "test";
167 map.mapping.insert(4, label.to_owned());
168
169 assert_eq!(map.get_label(4), label);
170 }
171
172 fn make_event(field_name: &str, value: Value) -> Event {
173 let mut values = std::collections::HashMap::<String, Value>::new();
174 values.insert(field_name.to_owned(), value);
175 Event::new(1, values)
176 }
177
178 #[test]
179 fn getter_should_return_value_if_value_exists() {
180 let mut getter = LabelGetter::new(LabelMap::new(), vec!["name".to_owned()]);
181 let event = make_event("name", Value::Str("test1".to_owned()));
182
183 let (field, value) = getter.get_label(&event).unwrap();
184
185 assert_eq!(field, "name");
186 assert_eq!(value, "test1");
187 }
188
189 #[test]
190 fn getter_should_not_return_value_if_field_does_not_exist() {
191 let mut getter = LabelGetter::new(LabelMap::new(), vec!["unknown".to_owned()]);
192 let event = make_event("name", Value::Str("test1".to_owned()));
193
194 let mapping = getter.get_label(&event);
195
196 assert!(mapping.is_none());
197 }
198
199 #[test]
200 fn getter_should_not_return_value_if_value_is_invalid() {
201 let mut getter = LabelGetter::new(LabelMap::new(), vec!["name".to_owned()]);
202 let event = make_event(
203 "name",
204 Value::Struct(Event::new(
205 1,
206 std::collections::HashMap::<String, Value>::new(),
207 )),
208 );
209
210 let mapping = getter.get_label(&event);
211
212 assert!(mapping.is_none());
213 }
214}