Skip to main content

gwr_track/
entity.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3//! A simulation entity.
4//!
5//! All parts of a model should contain an entity in order to maintain a
6//! hierarchy of simulation entities. They contain a name and a unique ID
7//! for tracing.
8
9use std::fmt;
10use std::rc::Rc;
11
12use crate::tracker::aka::{Aka, get_alternative_names};
13use crate::{Id, Tracker, create_id, destroy, trace};
14
15/// A capacity value and its units.
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct Capacity {
18    /// Capacity value.
19    pub value: usize,
20
21    /// Capacity units, for example "objects" or "bytes".
22    pub units: String,
23}
24
25impl Capacity {
26    /// Construct a new [`Capacity`].
27    #[must_use]
28    pub fn new(value: usize, units: impl Into<String>) -> Self {
29        Self {
30            value,
31            units: units.into(),
32        }
33    }
34}
35
36/// A simulation entity
37///
38/// An entity is a part of a hierarchical simulation in which it must have a
39/// parent. The simulation top-level should be created using `toplevel("name")`.
40///
41/// The entity is used when logging so that its unique ID can be emitted and
42/// it can determine which messages are emitted to both the binary and textual
43/// outputs.
44pub struct Entity {
45    /// Name of this entity.
46    pub name: String,
47
48    /// Optional parent entity (only the top-level should be None).
49    pub parent: Option<Rc<Entity>>,
50
51    /// Unique simulation identifier used for bin/log messages.
52    pub id: Id,
53
54    /// [`Tracker`] used to handle trace/log events.
55    pub tracker: Tracker,
56
57    /// Most verbose log level enabled for this entity by any tracker.
58    enabled_level: log::Level,
59}
60
61static JOIN: &str = "::";
62
63impl Entity {
64    /// Create a new entity.
65    #[must_use]
66    pub fn new(parent: &Rc<Entity>, name: &str) -> Self {
67        Self::new_with_renames(parent, name, None)
68    }
69
70    /// Create a new entity with a potential list of alternative names
71    #[must_use]
72    pub fn new_with_renames(parent: &Rc<Entity>, name: &str, aka: Option<&Aka>) -> Self {
73        let alternative_names = get_alternative_names(aka, name);
74        let mut full_name = parent.full_name();
75        full_name.push_str(JOIN);
76        full_name.push_str(name);
77
78        let tracker = parent.tracker.clone();
79        let id = create_id!(parent);
80        let enabled_level = tracker.add_entity(id, &full_name, alternative_names);
81
82        let entity = Self {
83            name: String::from(name),
84            parent: Some(parent.clone()),
85            id,
86            tracker,
87            enabled_level,
88        };
89        entity.track_create(parent.id, &full_name);
90
91        if let Some(alternative_names) = alternative_names {
92            for name in alternative_names {
93                trace!(entity ; "aka {name}");
94            }
95        }
96
97        entity
98    }
99
100    /// Returns the full hierarchical name of this entity
101    #[must_use]
102    pub fn full_name(&self) -> String {
103        match &self.parent {
104            Some(parent) => {
105                let mut name = parent.full_name();
106                name.push_str(JOIN);
107                name.push_str(self.name.as_str());
108                name
109            }
110            None => self.name.clone(),
111        }
112    }
113
114    /// Return whether trace-level events for this entity will be emitted.
115    #[must_use]
116    pub fn trace_enabled(&self) -> bool {
117        self.enabled_for(log::Level::Trace)
118    }
119
120    /// Return whether events at the given level will be emitted for this
121    /// entity.
122    #[must_use]
123    pub fn enabled_for(&self, level: log::Level) -> bool {
124        level <= self.enabled_level
125    }
126
127    /// Emit the capacity represented by this simulation entity.
128    pub fn track_capacity(&self, value: usize, units: impl Into<String>) {
129        self.tracker.capacity(self.id, Capacity::new(value, units));
130    }
131
132    /// Emit an enter event for an object.
133    pub fn track_enter(&self, entered: Id) {
134        self.tracker.enter(self.id, entered);
135    }
136
137    /// Emit an exit event for an object.
138    pub fn track_exit(&self, exited: Id) {
139        self.tracker.exit(self.id, exited);
140    }
141
142    fn track_create(&self, created_by: Id, full_name: &str) {
143        self.tracker.create_entity(created_by, self.id, full_name);
144    }
145}
146
147impl Drop for Entity {
148    fn drop(&mut self) {
149        destroy!(self);
150    }
151}
152
153impl fmt::Debug for Entity {
154    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155        f.debug_struct("Entity")
156            .field("name", &self.name)
157            .field("parent", &self.parent)
158            .field("id", &self.id)
159            .finish()
160    }
161}
162
163impl fmt::Display for Entity {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        if let Some(parent) = &self.parent {
166            parent.fmt(f).unwrap();
167            write!(f, "{}{}", JOIN, self.name)
168        } else {
169            write!(f, "{}", self.name)
170        }
171    }
172}
173
174/// Create the top-level entity. This should be the only entity without a
175/// parent.
176pub fn toplevel(tracker: &Tracker, name: &str) -> Rc<Entity> {
177    let id = tracker.unique_id();
178    let enabled_level = tracker.add_entity(id, name, None);
179    let top = Rc::new(Entity {
180        parent: None,
181        name: String::from(name),
182        id,
183        tracker: tracker.clone(),
184        enabled_level,
185    });
186    top.track_create(crate::NO_ID, name);
187    top
188}
189
190/// A monitor entity that is only allowed to emit value events.
191pub struct EntityMonitor {
192    /// The wrapped tracked entity.
193    pub entity: Rc<Entity>,
194
195    /// Unique simulation identifier used for bin/log messages.
196    pub id: Id,
197
198    /// Name of this monitor.
199    pub name: String,
200}
201
202impl EntityMonitor {
203    /// Create a new monitor entity.
204    #[must_use]
205    pub fn new(parent: &Rc<Entity>, name: &str) -> Self {
206        let mut full_name = parent.full_name();
207        full_name.push_str(JOIN);
208        full_name.push_str(name);
209
210        let id = create_id!(parent);
211        parent.tracker.add_entity(id, &full_name, None);
212
213        let monitor = Self {
214            entity: parent.clone(),
215            id,
216            name: String::from(name),
217        };
218
219        monitor.track_create(parent.id, &full_name);
220
221        monitor
222    }
223
224    fn track_create(&self, created_by: Id, full_name: &str) {
225        self.entity
226            .tracker
227            .create_monitor(created_by, self.id, full_name);
228    }
229
230    /// Emit a value event for this monitor.
231    pub fn track_value(&self, value: f64) {
232        self.entity.tracker.value(self.entity.id, value);
233    }
234}
235
236/// A child trace lane used to represent named activity intervals.
237pub struct EntityLane {
238    /// Parent entity that owns this lane.
239    pub entity: Rc<Entity>,
240
241    /// Unique simulation identifier used for trace events.
242    pub id: Id,
243
244    /// Name of this lane.
245    pub name: String,
246
247    active: bool,
248    active_activity: Option<Id>,
249    active_group: Option<Id>,
250}
251
252impl EntityLane {
253    /// Create a new lane under `parent`.
254    #[must_use]
255    pub fn new(parent: &Rc<Entity>, name: &str) -> Self {
256        let mut full_name = parent.full_name();
257        full_name.push_str(JOIN);
258        full_name.push_str(name);
259
260        let id = create_id!(parent);
261        let lane = Self {
262            entity: parent.clone(),
263            id,
264            name: String::from(name),
265            active: false,
266            active_activity: None,
267            active_group: None,
268        };
269
270        lane.track_create(parent.id, &full_name);
271
272        lane
273    }
274
275    fn track_create(&self, created_by: Id, full_name: &str) {
276        self.entity
277            .tracker
278            .create_lane(created_by, self.id, full_name);
279    }
280
281    /// Begin the named activity on this lane.
282    pub fn begin(&mut self, name: &str) {
283        let activity_id = create_id!(self.entity);
284        self.entity
285            .tracker
286            .begin_activity(activity_id, self.id, name);
287        self.active_activity = Some(activity_id);
288        self.active = true;
289    }
290
291    /// Begin the named activity as part of a group.
292    pub fn begin_in_group(&mut self, name: &str, group: &EntityGroup) {
293        let activity_id = create_id!(self.entity);
294        self.entity.tracker.add_to_group(activity_id, group.id);
295        self.active_activity = Some(activity_id);
296        self.active_group = Some(group.id);
297        self.entity
298            .tracker
299            .begin_activity(activity_id, self.id, name);
300        self.active = true;
301    }
302
303    /// End the current activity on this lane.
304    pub fn end(&mut self) {
305        if self.active {
306            let activity_id = self
307                .active_activity
308                .take()
309                .expect("active lane should have an activity ID");
310            self.entity.tracker.end_activity(activity_id);
311            if let Some(group_id) = self.active_group.take() {
312                self.entity.tracker.remove_from_group(activity_id, group_id);
313            }
314            self.active = false;
315        }
316    }
317}
318
319impl Drop for EntityLane {
320    fn drop(&mut self) {
321        self.end();
322        self.entity.tracker.destroy(self.entity.id, self.id);
323    }
324}
325
326/// A trace group used to associate related activities.
327pub struct EntityGroup {
328    /// Parent entity that owns this group.
329    pub entity: Rc<Entity>,
330
331    /// Unique simulation identifier used for trace events.
332    pub id: Id,
333
334    /// Name of this group.
335    pub name: String,
336}
337
338impl EntityGroup {
339    /// Create a new group under `parent`.
340    #[must_use]
341    pub fn new(parent: &Rc<Entity>, name: &str) -> Self {
342        let mut full_name = parent.full_name();
343        full_name.push_str(JOIN);
344        full_name.push_str(name);
345
346        let id = create_id!(parent);
347        let group = Self {
348            entity: parent.clone(),
349            id,
350            name: String::from(name),
351        };
352
353        group
354            .entity
355            .tracker
356            .create_group(parent.id, group.id, &full_name);
357
358        group
359    }
360}
361
362impl Drop for EntityGroup {
363    fn drop(&mut self) {
364        self.entity.tracker.destroy(self.entity.id, self.id);
365    }
366}
367
368/// The `GetEntity` trait is used to provide access to an objects [Entity]
369pub trait GetEntity {
370    /// Return the [Entity]
371    fn entity(&self) -> &Rc<Entity>;
372}
373
374impl GetEntity for EntityMonitor {
375    fn entity(&self) -> &Rc<Entity> {
376        &self.entity
377    }
378}