nichlink_run_method/runtime/trace/locals/
call_trace.rs1use std::collections::BTreeSet;
5
6use crate::registry_core::declaration::{SourceLocation, source_file_matches};
7use crate::runtime::trace::{CallSite, CallTrace, TraceMode};
8
9use super::{LocalId, LocalKind, LocalValue, Observation};
10
11impl CallTrace {
12 #[track_caller]
18 pub fn local(
19 &mut self,
20 name: impl Into<String>,
21 type_name: impl Into<String>,
22 value: impl std::fmt::Display,
23 kind: LocalKind,
24 ) -> LocalId {
25 let caller = std::panic::Location::caller();
26 self.local_at_callsite(
27 name,
28 type_name,
29 value.to_string(),
30 kind,
31 caller.file(),
32 caller.line(),
33 caller.column(),
34 )
35 }
36
37 pub fn local_at(
40 &mut self,
41 name: impl Into<String>,
42 type_name: impl Into<String>,
43 value: impl Into<String>,
44 kind: LocalKind,
45 source: SourceLocation,
46 ) -> LocalId {
47 self.local_with_observation(name, type_name, value, kind, source, Observation::Observed)
48 }
49
50 fn local_with_observation(
51 &mut self,
52 name: impl Into<String>,
53 type_name: impl Into<String>,
54 value: impl Into<String>,
55 kind: LocalKind,
56 source: SourceLocation,
57 observation: Observation,
58 ) -> LocalId {
59 if matches!(self.mode, TraceMode::Off) {
60 return LocalId(0);
61 }
62 let id = LocalId(self.next_local_id);
63 self.next_local_id = self.next_local_id.wrapping_add(1);
64 let local = LocalValue {
65 id: id.0,
66 name: name.into(),
67 type_name: type_name.into(),
68 value: value.into(),
69 kind,
70 source,
71 frame_id: self.current.last().copied(),
72 observation,
73 };
74 self.locals.push(local);
75 self.local_index.insert(id.0, self.locals.len() - 1);
76 let local = self.locals.last().expect("local was just pushed");
77 self.local_name_index
78 .entry(local.name.clone())
79 .or_default()
80 .push(id.0);
81 if let Some(frame_id) = local.frame_id
82 && let Some(function) = self.frame(frame_id).map(|frame| frame.call.function)
83 {
84 self.local_function_index
85 .entry(function)
86 .or_default()
87 .push(id.0);
88 }
89 id
90 }
91
92 pub fn inferred_local(
95 &mut self,
96 function: impl Into<String>,
97 name: impl Into<String>,
98 type_name: impl Into<String>,
99 line: u32,
100 ) -> LocalId {
101 if matches!(self.mode, TraceMode::Off) {
102 return LocalId(0);
103 }
104 let function = function.into();
105 let name = name.into();
106 self.local_with_observation(
107 format!("{function}::{name}"),
108 type_name,
109 "<not observed>",
110 LocalKind::Binding,
111 SourceLocation {
112 file: "<rustc-mir>",
113 line,
114 column: 0,
115 function: "<rustc-mir>",
116 },
117 Observation::Unobserved,
118 )
119 }
120
121 #[allow(clippy::too_many_arguments)]
130 pub fn local_at_callsite(
131 &mut self,
132 name: impl Into<String>,
133 type_name: impl Into<String>,
134 value: impl Into<String>,
135 kind: LocalKind,
136 file: &'static str,
137 line: u32,
138 column: u32,
139 ) -> LocalId {
140 if matches!(self.mode, TraceMode::Off) {
141 return LocalId(0);
142 }
143 let function = self
144 .current
145 .last()
146 .and_then(|id| self.frame(*id))
147 .map(|frame| frame.call.function)
148 .unwrap_or("<local>");
149 self.local_at(
150 name,
151 type_name,
152 value,
153 kind,
154 SourceLocation {
155 file,
156 line,
157 column,
158 function,
159 },
160 )
161 }
162
163 pub fn locals(&self) -> &[LocalValue] {
166 &self.locals
167 }
168
169 pub fn path_for_local(&self, id: LocalId) -> Vec<CallSite> {
172 self.find_local(id)
173 .and_then(|local| local.frame_id)
174 .map_or_else(Vec::new, |frame_id| self.path_for(frame_id))
175 }
176
177 pub fn find_local(&self, id: LocalId) -> Option<&LocalValue> {
180 self.local_index
181 .get(&id.0)
182 .and_then(|index| self.locals.get(*index))
183 }
184
185 pub fn matching_locals(&self, query: &str) -> Vec<&LocalValue> {
192 let query = query.trim().to_ascii_lowercase();
193 if query.is_empty() {
194 return self.locals.iter().collect();
195 }
196 let exact_ids = self
197 .local_name_index
198 .iter()
199 .filter(|(name, _)| name.to_ascii_lowercase() == query)
200 .flat_map(|(_, ids)| ids.iter().copied())
201 .chain(
202 self.local_function_index
203 .iter()
204 .filter(|(function, _)| function.to_ascii_lowercase() == query)
205 .flat_map(|(_, ids)| ids.iter().copied()),
206 )
207 .collect::<BTreeSet<_>>();
208 if !exact_ids.is_empty() {
209 return exact_ids
210 .into_iter()
211 .filter_map(|id| self.find_local(LocalId(id)))
212 .collect();
213 }
214 self.locals
215 .iter()
216 .filter(|local| {
217 local.name.to_ascii_lowercase().contains(&query)
218 || local.type_name.to_ascii_lowercase().contains(&query)
219 || local.value.to_ascii_lowercase().contains(&query)
220 || source_file_matches(local.source.file, &query)
221 || local.source.function.to_ascii_lowercase().contains(&query)
222 || local
223 .frame_id
224 .is_some_and(|frame_id| self.frame_chain_matches(frame_id, &query))
225 })
226 .collect()
227 }
228}