reactor_rt/scheduler/
debug.rs

1/*
2 * Copyright (c) 2021, TU Dresden.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice,
8 *    this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
22 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25use core::any::type_name;
26use std::borrow::Cow;
27use std::collections::HashMap;
28use std::fmt::{Display, Formatter, Result};
29use std::ops::Range;
30
31use crate::vecmap::VecMap;
32use index_vec::{Idx, IndexVec};
33
34use crate::assembly::{ReactorInitializer, TriggerId};
35use crate::{GlobalReactionId, ReactorId};
36
37/// Maps IDs to debug information, stores all the debug info.
38/// This is built during assembly.
39/// At runtime, this is only used to format debug messages and
40/// perform debug assertions, so compactness is more important
41/// than speed of the methods.
42pub(crate) struct DebugInfoRegistry {
43    /// Maps reactor ids to their debug info.
44    reactor_infos: IndexVec<ReactorId, ReactorDebugInfo>,
45
46    /// Maps a ReactorId to the last TriggerId (exclusive) it occupies.
47    /// This is used to get the ReactorId back from a TriggerId.
48    reactor_bound: IndexVec<ReactorId, TriggerId>,
49
50    /// Labels of each trigger, every trigger id in the program
51    /// is registered here.
52    trigger_infos: IndexVec<TriggerId, Cow<'static, str>>,
53
54    /// Maps each reactor id to the id of its container.
55    /// The main reactor is not registered.
56    reactor_container: VecMap<ReactorId, ReactorId>,
57
58    main_reactor: Option<ReactorId>,
59
60    // todo better data structure, eg IndexVec<ReactorId, IndexVec<LocalReactionId, _>>
61    /// Labels of each reaction, only reactions that have one are in here.
62    reaction_labels: HashMap<GlobalReactionId, Cow<'static, str>>,
63}
64
65/// The reactor ID, and the local index within the reactor.
66/// We don't use GlobalId because the second component is not
67/// a LocalReactionId, for trigger ids it may be as big as
68/// usize, so we inflate LocalReactionId to usize.
69type RawId = (ReactorId, usize);
70
71impl DebugInfoRegistry {
72    pub fn new() -> Self {
73        let mut ich = Self {
74            reactor_infos: Default::default(),
75            reactor_bound: Default::default(),
76            trigger_infos: Default::default(),
77            reaction_labels: Default::default(),
78            reactor_container: Default::default(),
79            main_reactor: None,
80        };
81
82        assert_eq!(ich.trigger_infos.push(Cow::Borrowed("startup")), TriggerId::STARTUP);
83        assert_eq!(ich.trigger_infos.push(Cow::Borrowed("shutdown")), TriggerId::SHUTDOWN);
84
85        ich
86    }
87}
88
89impl DebugInfoRegistry {
90    pub fn get_debug_info(&self, id: ReactorId) -> &ReactorDebugInfo {
91        &self.reactor_infos[id]
92    }
93
94    /// Format the id of a component.
95    fn fmt_component_path<'a>(
96        &'a self,
97        id: RawId,
98        label: Option<&'a Cow<'static, str>>,
99        always_display_idx: bool,
100    ) -> impl Display + 'a {
101        struct PathFmt<'a> {
102            debug: &'a DebugInfoRegistry,
103            id: RawId,
104            label: Option<&'a Cow<'static, str>>,
105            /// If true, the index is part of the output,
106            /// even if the label is present.
107            always_display_idx: bool,
108        }
109        use std::fmt::*;
110        impl Display for PathFmt<'_> {
111            #[inline]
112            fn fmt(&self, f: &mut Formatter<'_>) -> Result {
113                write!(f, "{}", self.debug.get_debug_info(self.id.0))?;
114                if let Some(label) = &self.label {
115                    if self.always_display_idx {
116                        write!(f, "{}@{}", self.id.1, label)
117                    } else {
118                        write!(f, "{}", label)
119                    }
120                } else {
121                    write!(f, "{}", self.id.1)
122                }
123            }
124        }
125
126        PathFmt { debug: self, id, label, always_display_idx }
127    }
128
129    #[inline]
130    pub fn fmt_reaction(&self, id: GlobalReactionId) -> impl Display + '_ {
131        let raw = (id.0.container(), id.0.local().index());
132        self.fmt_component_path(raw, self.reaction_labels.get(&id), true)
133    }
134
135    #[inline]
136    pub fn fmt_component(&self, id: TriggerId) -> impl Display + '_ {
137        self.fmt_component_path(self.raw_id_of_trigger(id), Some(&self.trigger_infos[id]), false)
138    }
139
140    #[inline]
141    pub fn get_container(&self, id: ReactorId) -> Option<ReactorId> {
142        let container = self.reactor_container.get(&id);
143        debug_assert!(container.is_some() || self.is_main(id));
144        container.cloned()
145    }
146    #[inline]
147    pub fn get_trigger_container(&self, id: TriggerId) -> Option<ReactorId> {
148        match id {
149            TriggerId::SHUTDOWN | TriggerId::STARTUP => None,
150            _ => Some(self.raw_id_of_trigger(id).0),
151        }
152    }
153
154    #[inline]
155    pub fn is_main(&self, id: ReactorId) -> bool {
156        self.main_reactor.unwrap() == id
157    }
158
159    fn raw_id_of_trigger(&self, id: TriggerId) -> RawId {
160        match id {
161            // Pretend startup and shutdown are in the last reactor.
162            // For programs built with LFC, it's the main reactor.
163            TriggerId::STARTUP | TriggerId::SHUTDOWN => {
164                let last_reactor = self.reactor_infos.last_idx();
165                let max_idx = *self.reactor_bound.last().unwrap();
166                let max_local_idx = max_idx.index() - self.get_reactor_lower_bound(last_reactor).index();
167                (last_reactor, max_local_idx + id.index())
168            }
169
170            id => {
171                match self.reactor_bound.binary_search(&id) {
172                    // we're the upper bound of some reactor `rid`,
173                    // ie, we're the first component of the next reactor.
174                    Ok(rid) => (rid.plus(1), 0usize),
175                    // Here, rid is the reactor which contains the trigger.
176                    // Eg if you have reactor_bound=[2, 4],
177                    // that corresponds to two reactors [2..2, 2..4].
178                    // If you ask for 2, it will take the branch Ok above.
179                    // If you ask for 3, it will fail with Err(0), and reactor_bound[0]==2
180                    // is actually the index of the reactor.
181                    Err(rid) => (rid, id.index() - self.get_reactor_lower_bound(rid).index()),
182                }
183            }
184        }
185    }
186
187    fn get_reactor_lower_bound(&self, rid: ReactorId) -> TriggerId {
188        rid.index()
189            .checked_sub(1)
190            .map(|ix| self.reactor_bound[ix])
191            .unwrap_or(TriggerId::FIRST_REGULAR)
192    }
193
194    pub(crate) fn record_trigger(&mut self, id: TriggerId, name: Cow<'static, str>) {
195        let ix = self.trigger_infos.push(name);
196        debug_assert_eq!(ix, id);
197    }
198
199    pub(crate) fn record_reaction(&mut self, id: GlobalReactionId, name: Cow<'static, str>) {
200        let existing = self.reaction_labels.insert(id, name);
201        debug_assert!(existing.is_none())
202    }
203
204    pub(crate) fn record_reactor(&mut self, id: ReactorId, debug: ReactorDebugInfo) {
205        let ix = self.reactor_infos.push(debug);
206        debug_assert_eq!(ix, id);
207    }
208
209    pub(crate) fn record_main_reactor(&mut self, id: ReactorId) {
210        let prev = self.main_reactor.replace(id);
211        assert!(prev.is_none(), "cannot call record_main twice");
212    }
213
214    pub(crate) fn record_reactor_container(&mut self, parent: ReactorId, child: ReactorId) {
215        let ix = self.reactor_container.insert(child, parent);
216        debug_assert!(ix.is_none(), "overwrote reactor");
217    }
218
219    pub(crate) fn set_id_range(&mut self, id: ReactorId, range: Range<TriggerId>) {
220        assert!(range.start <= range.end, "Malformed range {:?}", range);
221        assert!(range.start >= TriggerId::FIRST_REGULAR, "Trigger IDs 0-1 are reserved");
222
223        let ix = self.reactor_bound.push(range.end);
224        assert_eq!(ix, id);
225    }
226}
227
228/// Debug information for a single reactor.
229pub(crate) struct ReactorDebugInfo {
230    /// Type name
231    #[allow(unused)]
232    pub type_name: &'static str,
233    /// Simple name of the instantiation (last segment of the path)
234    #[allow(unused)]
235    pub inst_name: &'static str,
236    /// Path to this instantiation, with trailing slash (eg `"/parent/child/"`)
237    inst_path: String,
238}
239
240impl ReactorDebugInfo {
241    #[cfg(test)]
242    pub(crate) fn test() -> Self {
243        Self::root::<()>()
244    }
245
246    #[cfg(test)]
247    pub(crate) fn test_named(inst_path: impl Into<String>) -> Self {
248        let mut inst_path = inst_path.into();
249        inst_path.push('/');
250        Self {
251            type_name: "unknown",
252            inst_name: "unknown",
253            inst_path,
254        }
255    }
256
257    pub(crate) fn root<R>() -> Self {
258        Self {
259            type_name: type_name::<R>(),
260            inst_name: "/",
261            inst_path: "/".into(),
262        }
263    }
264
265    pub(crate) fn derive<R: ReactorInitializer>(&self, inst_name: &'static str) -> Self {
266        Self {
267            type_name: type_name::<R::Wrapped>(),
268            inst_name,
269            inst_path: format!("{}{}/", self.inst_path, inst_name),
270        }
271    }
272
273    pub(crate) fn derive_bank_item<R: ReactorInitializer>(&self, inst_name: &'static str, bank_idx: usize) -> Self {
274        Self {
275            type_name: type_name::<R::Wrapped>(),
276            inst_name,
277            inst_path: format!("{}{}[{}]/", self.inst_path, inst_name, bank_idx),
278        }
279    }
280}
281
282impl Display for ReactorDebugInfo {
283    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
284        write!(f, "{}", self.inst_path)
285    }
286}
287
288#[cfg(test)]
289pub mod test {
290    use crate::assembly::TriggerId;
291    use crate::{DebugInfoRegistry, ReactorDebugInfo, ReactorId};
292
293    #[test]
294    fn test_raw_id_from_trigger() -> Result<(), ()> {
295        let mut debug = DebugInfoRegistry::new();
296        let mut trigger_id = TriggerId::FIRST_REGULAR;
297        let reactor_0 = ReactorId::new(0);
298        let first_trigger = trigger_id;
299        debug.record_reactor(reactor_0, ReactorDebugInfo::test_named("foo"));
300        debug.record_trigger(trigger_id.get_and_incr()?, "t0".into());
301        debug.record_trigger(trigger_id.get_and_incr()?, "t1".into());
302        debug.set_id_range(reactor_0, first_trigger..trigger_id);
303
304        let reactor_1 = ReactorId::new(1);
305        let first_trigger = trigger_id;
306        debug.record_reactor(reactor_1, ReactorDebugInfo::test_named("foo1"));
307        debug.record_trigger(trigger_id.get_and_incr()?, "t0".into());
308        debug.record_trigger(trigger_id.get_and_incr()?, "t1".into());
309        debug.set_id_range(reactor_1, first_trigger..trigger_id);
310
311        let mut trigger_id = TriggerId::FIRST_REGULAR;
312        assert_eq!((reactor_0, 0), debug.raw_id_of_trigger(trigger_id.get_and_incr()?));
313        assert_eq!((reactor_0, 1), debug.raw_id_of_trigger(trigger_id.get_and_incr()?));
314        assert_eq!((reactor_1, 0), debug.raw_id_of_trigger(trigger_id.get_and_incr()?));
315        assert_eq!((reactor_1, 1), debug.raw_id_of_trigger(trigger_id.get_and_incr()?));
316
317        assert_eq!((reactor_1, 2), debug.raw_id_of_trigger(TriggerId::STARTUP));
318        assert_eq!((reactor_1, 3), debug.raw_id_of_trigger(TriggerId::SHUTDOWN));
319
320        Ok(())
321    }
322}