hawktracer_converter_lib/
label_mapping.rs

1use hawktracer_parser::{CoreEventKlassId, Event, Value};
2use std::io::BufRead;
3
4// TODO: SUPPORT MAPPING EVENT
5
6#[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); // TODO support data[0] (category)?
46        }
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    // TODO TEST ME PLEASE!
79    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                // TODO We should have KlassInfo event wrapper with get_klass_name method in parser
86                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}