Skip to main content

gwr_track/
trace_visitor.rs

1// Copyright (c) 2020 Graphcore Ltd. All rights reserved.
2
3//! This module provides helper functions for dealing with Cap'n Proto binary
4//! data.
5
6use std::io::BufRead;
7
8use capnp::serialize_packed;
9
10use crate::entity::Capacity;
11use crate::gwr_track_capnp::log::LogLevel;
12use crate::{Id, gwr_track_capnp};
13
14/// The `TraceVisitor` trait is the interface that allows a user to see all the
15/// events as a binary file is processed.
16///
17/// Note that the ID will be [NO_ID](../../gwr_track/constant.NO_ID.html) if
18/// the user hasn't set it.
19pub trait TraceVisitor {
20    /// A log event.
21    ///
22    /// # Arguments
23    ///
24    /// * `id` - The originator of this event.
25    /// * `level` - The logging level of the message.
26    /// * `message` - The string to emit with this event.
27    fn log(&mut self, id: Id, level: log::Level, message: &str) {
28        // Remove the unused variable warnings
29        let _ = id;
30        let _ = level;
31        let _ = message;
32    }
33
34    /// The creation of an entity.
35    ///
36    /// # Arguments
37    ///
38    /// * `created_by` - ID of the entity causing the creation.
39    /// * `id` - The originator of this event.
40    /// * `name` - Name of the entity being created.
41    fn create_entity(&mut self, created_by: Id, id: Id, name: &str) {
42        let _ = created_by;
43        let _ = id;
44        let _ = name;
45    }
46
47    /// The creation of a monitor.
48    ///
49    /// # Arguments
50    ///
51    /// * `created_by` - ID of the entity causing the creation.
52    /// * `id` - The originator of this event.
53    /// * `name` - Name of the monitor being created.
54    fn create_monitor(&mut self, created_by: Id, id: Id, name: &str) {
55        let _ = created_by;
56        let _ = id;
57        let _ = name;
58    }
59
60    /// The creation of a lane.
61    ///
62    /// # Arguments
63    ///
64    /// * `created_by` - ID of the entity causing the creation.
65    /// * `id` - The originator of this event.
66    /// * `name` - Name of the lane being created.
67    fn create_lane(&mut self, created_by: Id, id: Id, name: &str) {
68        let _ = created_by;
69        let _ = id;
70        let _ = name;
71    }
72
73    /// The creation of a group.
74    ///
75    /// # Arguments
76    ///
77    /// * `created_by` - ID of the entity causing the creation.
78    /// * `id` - The originator of this event.
79    /// * `name` - Name of the group being created.
80    fn create_group(&mut self, created_by: Id, id: Id, name: &str) {
81        let _ = created_by;
82        let _ = id;
83        let _ = name;
84    }
85
86    /// The creation of an object.
87    ///
88    /// # Arguments
89    ///
90    /// * `created_by` - ID of the entity causing the creation.
91    /// * `id` - The originator of this event.
92    /// * `size` - Size of the created object.
93    /// * `units` - Units for the created object size.
94    /// * `req_type` - The type of request being traced (Read, Write, etc).
95    /// * `details` - Additional detail for the created object.
96    fn create_object(
97        &mut self,
98        created_by: Id,
99        id: Id,
100        size: usize,
101        units: &str,
102        req_type: u8,
103        details: &str,
104    ) {
105        let _ = created_by;
106        let _ = id;
107        let _ = size;
108        let _ = units;
109        let _ = req_type;
110        let _ = details;
111    }
112
113    /// The destruction of a unique ID.
114    ///
115    /// # Arguments
116    ///
117    /// * `destroyed_by` - ID of the entity causing the destruction.
118    /// * `id` - The originator of this event.
119    fn destroy(&mut self, destroyed_by: Id, id: Id) {
120        // Remove the unused variable warnings
121        let _ = destroyed_by;
122        let _ = id;
123    }
124
125    /// One entity is connected to another.
126    ///
127    /// # Arguments
128    ///
129    /// * `connect_from` - ID of the entity being connected from.
130    /// * `connect_to` - ID of the entity being connected to.
131    fn connect(&mut self, connect_from: Id, connect_to: Id) {
132        // Remove the unused variable warnings
133        let _ = connect_from;
134        let _ = connect_to;
135    }
136
137    /// A ID is entered (e.g. start of a function or block).
138    ///
139    /// # Arguments
140    ///
141    /// * `id` - The originator of this event.
142    /// * `entered` - The ID of the entity entering.
143    fn enter(&mut self, id: Id, entered: Id) {
144        // Remove the unused variable warnings
145        let _ = id;
146        let _ = entered;
147    }
148
149    /// A ID is exited (e.g. end of a function or block).
150    ///
151    /// # Arguments
152    ///
153    /// * `id` - The originator of this event.
154    /// * `exited` - The ID of the entity exiting.
155    fn exit(&mut self, id: Id, exited: Id) {
156        // Remove the unused variable warnings
157        let _ = id;
158        let _ = exited;
159    }
160
161    /// A value has been set by the specified ID.
162    ///
163    /// # Arguments
164    ///
165    /// * `id` - The originator of this event.
166    /// * `value` - The value.
167    fn value(&mut self, id: Id, value: f64) {
168        // Remove the unused variable warnings
169        let _ = id;
170        let _ = value;
171    }
172
173    /// The specified ID has been added to a group.
174    ///
175    /// # Arguments
176    ///
177    /// * `id` - The ID added to the group.
178    /// * `group_id` - The group ID.
179    fn add_to_group(&mut self, id: Id, group_id: Id) {
180        let _ = id;
181        let _ = group_id;
182    }
183
184    /// The specified ID has been removed from a group.
185    ///
186    /// # Arguments
187    ///
188    /// * `id` - The ID removed from the group.
189    /// * `group_id` - The group ID.
190    fn remove_from_group(&mut self, id: Id, group_id: Id) {
191        let _ = id;
192        let _ = group_id;
193    }
194
195    /// A named activity has begun on the specified lane.
196    ///
197    /// # Arguments
198    ///
199    /// * `activity` - The activity identity.
200    /// * `lane` - The lane on which the activity is starting.
201    /// * `name` - The activity name.
202    fn begin_activity(&mut self, activity: Id, lane: Id, name: &str) {
203        let _ = activity;
204        let _ = lane;
205        let _ = name;
206    }
207
208    /// The specivied activity has ended.
209    ///
210    /// # Arguments
211    ///
212    /// * `activity` - The activity identity.
213    fn end_activity(&mut self, activity: Id) {
214        let _ = activity;
215    }
216
217    /// A capacity has been set for the specified ID.
218    ///
219    /// # Arguments
220    ///
221    /// * `id` - The originator of this event.
222    /// * `capacity` - The entity capacity and its units.
223    fn capacity(&mut self, id: Id, capacity: Capacity) {
224        // Remove the unused variable warnings
225        let _ = id;
226        let _ = capacity;
227    }
228
229    /// Advance simulation time.
230    ///
231    /// # Arguments
232    ///
233    /// * `id` - The originator of this event.
234    /// * `time_ns` - The new simulation time in `ns`.
235    fn time(&mut self, id: Id, time_ns: f64) {
236        // Remove the unused variable warnings
237        let _ = id;
238        let _ = time_ns;
239    }
240}
241
242/// Process a given Cap'n Proto file calling the visitor for each event found.
243///
244/// # Examples
245///
246/// A simple visitor that will count how many IDs are used.
247/// ```no_run
248/// # use std::error::Error;
249/// use std::fs::File;
250/// use std::io::BufReader;
251///
252/// use gwr_track::Id;
253/// use gwr_track::trace_visitor::{TraceVisitor, process_capnp};
254///
255/// struct IdCounter {
256///     pub count: usize,
257/// }
258///
259/// impl IdCounter {
260///     fn new() -> Self {
261///         Self { count: 0 }
262///     }
263/// }
264///
265/// impl TraceVisitor for IdCounter {
266///     fn create_entity(&mut self, _created_by: Id, _id: Id, _name: &str) {
267///         self.count += 1;
268///     }
269/// }
270///
271/// # fn main() -> Result<(), Box<dyn Error>> {
272/// let f = File::open("capnp.bin")?;
273/// let mut reader = BufReader::new(f);
274/// let mut visitor = IdCounter::new();
275/// process_capnp(&mut reader, &mut visitor);
276/// println!("{} IDs seen", visitor.count);
277/// #
278/// # Ok(())
279/// # }
280/// ```
281pub fn process_capnp<R>(mut reader: R, visitor: &mut dyn TraceVisitor)
282where
283    R: BufRead,
284{
285    while let Ok(event_reader) =
286        serialize_packed::read_message(&mut reader, ::capnp::message::ReaderOptions::new())
287    {
288        let event = event_reader
289            .get_root::<gwr_track_capnp::event::Reader>()
290            .expect("should be able to parse event");
291
292        let id = Id(event.get_id());
293        match event.which() {
294            Ok(gwr_track_capnp::event::Which::Log(builder)) => handle_log(visitor, id, builder),
295            Ok(gwr_track_capnp::event::Which::Create(builder)) => {
296                handle_create(visitor, id, builder);
297            }
298            Ok(gwr_track_capnp::event::Which::Destroy(destroyed_by)) => {
299                handle_destroy(visitor, id, destroyed_by);
300            }
301            Ok(gwr_track_capnp::event::Which::Connect(connect_to)) => {
302                handle_connect(visitor, id, connect_to);
303            }
304            Ok(gwr_track_capnp::event::Which::Enter(entered)) => handle_enter(visitor, id, entered),
305            Ok(gwr_track_capnp::event::Which::Exit(exited)) => handle_exit(visitor, id, exited),
306            Ok(gwr_track_capnp::event::Which::Value(value)) => handle_value(visitor, id, value),
307            Ok(gwr_track_capnp::event::Which::AddToGroup(group_id)) => {
308                handle_add_to_group(visitor, id, group_id);
309            }
310            Ok(gwr_track_capnp::event::Which::RemoveFromGroup(group_id)) => {
311                handle_remove_from_group(visitor, id, group_id);
312            }
313            Ok(gwr_track_capnp::event::Which::BeginActivity(begin_activity)) => {
314                handle_begin_activity(visitor, id, begin_activity);
315            }
316            Ok(gwr_track_capnp::event::Which::EndActivity(())) => {
317                handle_end_activity(visitor, id);
318            }
319            Ok(gwr_track_capnp::event::Which::Capacity(capacity)) => {
320                handle_capacity(visitor, id, capacity);
321            }
322            Ok(gwr_track_capnp::event::Which::Time(time)) => handle_time(visitor, id, time),
323            Err(e) => {
324                panic!("should be able to parse event ({e})");
325            }
326        }
327    }
328}
329
330fn handle_log(
331    visitor: &mut dyn TraceVisitor,
332    id: Id,
333    builder: capnp::Result<gwr_track_capnp::log::Reader<'_>>,
334) {
335    let access = builder.expect("should be able to parse Log event");
336    visitor.log(
337        id,
338        to_log_level(
339            access
340                .get_level()
341                .expect("should be able to parse Log level"),
342        ),
343        access
344            .get_message()
345            .expect("should be able to parse Log message")
346            .to_str()
347            .expect("Log message should be valid UTF-8 string"),
348    );
349}
350
351fn handle_create(
352    visitor: &mut dyn TraceVisitor,
353    id: Id,
354    builder: capnp::Result<gwr_track_capnp::create::Reader<'_>>,
355) {
356    let access = builder.expect("should be able to parse Create event");
357    let created_id = Id(access.get_id());
358    match access.which() {
359        Ok(gwr_track_capnp::create::Which::Entity(entity)) => {
360            let entity = entity.expect("should be able to parse Create Entity");
361            visitor.create_entity(
362                id,
363                created_id,
364                entity
365                    .get_name()
366                    .expect("should be able to parse Entity name")
367                    .to_str()
368                    .expect("Create Entity name should be valid UTF-8 string"),
369            );
370        }
371        Ok(gwr_track_capnp::create::Which::Monitor(monitor)) => {
372            let monitor = monitor.expect("should be able to parse Create Monitor");
373            visitor.create_monitor(
374                id,
375                created_id,
376                monitor
377                    .get_name()
378                    .expect("should be able to parse Monitor name")
379                    .to_str()
380                    .expect("Create Monitor name should be valid UTF-8 string"),
381            );
382        }
383        Ok(gwr_track_capnp::create::Which::Lane(lane)) => {
384            let lane = lane.expect("should be able to parse Create Lane");
385            visitor.create_lane(
386                id,
387                created_id,
388                lane.get_name()
389                    .expect("should be able to parse Lane name")
390                    .to_str()
391                    .expect("Create Lane name should be valid UTF-8 string"),
392            );
393        }
394        Ok(gwr_track_capnp::create::Which::Group(group)) => {
395            let group = group.expect("should be able to parse Create Group");
396            visitor.create_group(
397                id,
398                created_id,
399                group
400                    .get_name()
401                    .expect("should be able to parse Group name")
402                    .to_str()
403                    .expect("Create Group name should be valid UTF-8 string"),
404            );
405        }
406        Ok(gwr_track_capnp::create::Which::Object(object)) => {
407            let object = object.expect("should be able to parse Create Object");
408            visitor.create_object(
409                id,
410                created_id,
411                object.get_size() as usize,
412                object
413                    .get_units()
414                    .expect("should be able to parse Object units")
415                    .to_str()
416                    .expect("Create Object units should be valid UTF-8 string"),
417                object.get_type(),
418                object
419                    .get_details()
420                    .expect("should be able to parse Object details")
421                    .to_str()
422                    .expect("Create Object details should be valid UTF-8 string"),
423            );
424        }
425        Err(e) => panic!("should be able to parse create event ({e})"),
426    }
427}
428
429fn handle_destroy(visitor: &mut dyn TraceVisitor, id: Id, destroyed_by: u64) {
430    visitor.destroy(id, Id(destroyed_by));
431}
432
433fn handle_connect(visitor: &mut dyn TraceVisitor, id: Id, connect_to: u64) {
434    visitor.connect(id, Id(connect_to));
435}
436
437fn handle_enter(visitor: &mut dyn TraceVisitor, id: Id, entered: u64) {
438    visitor.enter(id, Id(entered));
439}
440
441fn handle_exit(visitor: &mut dyn TraceVisitor, id: Id, exited: u64) {
442    visitor.exit(id, Id(exited));
443}
444
445fn handle_value(visitor: &mut dyn TraceVisitor, id: Id, value: f64) {
446    visitor.value(id, value);
447}
448
449fn handle_add_to_group(visitor: &mut dyn TraceVisitor, id: Id, group_id: u64) {
450    visitor.add_to_group(id, Id(group_id));
451}
452
453fn handle_remove_from_group(visitor: &mut dyn TraceVisitor, id: Id, group_id: u64) {
454    visitor.remove_from_group(id, Id(group_id));
455}
456
457fn handle_begin_activity(
458    visitor: &mut dyn TraceVisitor,
459    id: Id,
460    begin_activity: capnp::Result<gwr_track_capnp::begin_activity::Reader<'_>>,
461) {
462    let begin_activity = begin_activity.expect("should be able to parse BeginActivity event");
463    visitor.begin_activity(
464        id,
465        Id(begin_activity.get_lane()),
466        begin_activity
467            .get_name()
468            .expect("should be able to parse activity name")
469            .to_str()
470            .expect("Activity name should be valid UTF-8 string"),
471    );
472}
473
474fn handle_end_activity(visitor: &mut dyn TraceVisitor, id: Id) {
475    visitor.end_activity(id);
476}
477
478fn handle_capacity(
479    visitor: &mut dyn TraceVisitor,
480    id: Id,
481    capacity: capnp::Result<gwr_track_capnp::capacity::Reader<'_>>,
482) {
483    let capacity = capacity.expect("should be able to parse Capacity event");
484    visitor.capacity(
485        id,
486        Capacity::new(
487            capacity.get_value() as usize,
488            capacity
489                .get_units()
490                .expect("should be able to parse Capacity units")
491                .to_str()
492                .expect("Capacity units should be valid UTF-8 string"),
493        ),
494    );
495}
496
497fn handle_time(visitor: &mut dyn TraceVisitor, id: Id, time: f64) {
498    visitor.time(id, time);
499}
500
501fn to_log_level(level: LogLevel) -> log::Level {
502    match level {
503        LogLevel::Error => log::Level::Error,
504        LogLevel::Warn => log::Level::Warn,
505        LogLevel::Info => log::Level::Info,
506        LogLevel::Debug => log::Level::Debug,
507        LogLevel::Trace => log::Level::Trace,
508    }
509}