Skip to main content

nichlink_run_method/runtime/trace/locals/
call_trace.rs

1//! Local-value recording and query methods on `CallTrace`.
2//! `CallTrace` 上的局部值记录与查询方法。
3
4use 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    /// Record a local, taking file/line/column from the caller's location.
13    /// 记录一个局部值,文件、行、列取自调用方位置。
14    ///
15    /// Returns `LocalId(0)` and stores nothing when the trace is `Off`.
16    /// 追踪为 `Off` 时不存储任何内容并返回 `LocalId(0)`。
17    #[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    /// Record an observed local at an explicit source location.
38    /// 在显式源码位置记录一个已观测局部值。
39    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    /// Add a MIR candidate whose runtime value was not observed.
93    /// 添加运行时未观察到的 MIR 候选局部变量。
94    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    /// Record an observed local with an explicit callsite.
122    /// 用显式调用点记录一个已观测局部值。
123    ///
124    /// This is the `#[track_caller]`-free entry point for adapters, and it is
125    /// what `local` delegates to; the function name is read from the active
126    /// frame, or `"<local>"` outside any traced call.
127    /// 这是供适配器使用的、不依赖 `#[track_caller]` 的入口,也是 `local` 的委托目标;
128    /// 函数名取自活动帧,不在任何被追踪调用内时为 `"<local>"`。
129    #[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    /// Every local recorded so far, in recording order.
164    /// 按记录顺序返回目前记录的全部局部值。
165    pub fn locals(&self) -> &[LocalValue] {
166        &self.locals
167    }
168
169    /// The root-to-leaf call path that encloses `id`, empty when unknown.
170    /// 包含 `id` 的根到叶调用路径;未知时为空。
171    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    /// Look up one local by id, `None` when the id describes no live evidence.
178    /// 按 id 查找一个局部值;该 id 不对应任何现存证据时为 `None`。
179    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    /// Locals matching a case-insensitive name, function, type, or value query.
186    /// 匹配大小写不敏感的名称、函数、类型或取值查询的局部值。
187    ///
188    /// An empty query returns every local; an exact name or function match wins
189    /// over the substring search, so a caller sees the precise hit first.
190    /// 空查询返回全部局部值;名称或函数的精确匹配优先于子串搜索,调用方先看到精确命中。
191    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}