1use alloc::{
2 borrow::Cow,
3 boxed::Box,
4 collections::{BTreeMap, BTreeSet, VecDeque},
5 string::{String, ToString},
6 sync::Arc,
7 vec::Vec,
8};
9use core::{cell::OnceCell, fmt};
10#[cfg(feature = "std")]
11use std::path::{Path, PathBuf};
12
13use miden_core::operations::AssemblyOp;
14use miden_debug_types::{Location, SourceFile, SourceManager, SourceSpan, Uri};
15use miden_mast_package::debug_info::{DebugSourceInlineCall, DebugSourceNodeId, PackageDebugInfo};
16use miden_processor::{ContextId, SourceInlineCallContext, operation::Operation, trace::RowIndex};
17use miden_utils_sync::RwLock;
18
19use crate::Event;
20
21#[derive(Copy, Clone, Debug, Eq, PartialEq)]
22pub enum ControlFlowOp {
23 Span,
24 Respan,
25 Join,
26 Split,
27 End,
28}
29
30pub struct StepInfo<'a> {
31 pub op: Option<Operation>,
32 pub control: Option<ControlFlowOp>,
33 pub asmop: Option<&'a AssemblyOp>,
34 pub clk: RowIndex,
35 pub ctx: ContextId,
36 pub inline_frames: &'a [InlineCallFrame],
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct InlineCallFrame {
41 name: Arc<str>,
42 call_site: Location,
43}
44
45impl InlineCallFrame {
46 pub fn name(&self) -> &str {
47 &self.name
48 }
49
50 pub fn call_site(&self) -> &Location {
51 &self.call_site
52 }
53
54 pub fn display_name(&self) -> String {
55 demangle(&self.name)
56 }
57}
58
59#[derive(Debug, Copy, Clone, PartialEq, Eq)]
60pub enum LogicalFrameKind {
61 Physical,
62 Inline,
63}
64
65#[derive(Debug, Clone)]
66enum LogicalFrameLocation {
67 Assembly(Location),
68 Resolved(ResolvedLocation),
69}
70
71#[derive(Debug, Clone)]
72pub struct LogicalStackFrame {
73 name: Arc<str>,
74 kind: LogicalFrameKind,
75 location: Option<LogicalFrameLocation>,
76 physical_index: usize,
77}
78
79impl LogicalStackFrame {
80 pub fn name(&self) -> &str {
81 &self.name
82 }
83
84 pub fn kind(&self) -> LogicalFrameKind {
85 self.kind
86 }
87
88 pub fn physical_index(&self) -> usize {
89 self.physical_index
90 }
91
92 pub fn display_name(&self) -> String {
93 match self.kind {
94 LogicalFrameKind::Physical => self.name.to_string(),
95 LogicalFrameKind::Inline => format!("[inlined] {}", self.name),
96 }
97 }
98
99 pub fn resolved(&self, source_manager: &dyn SourceManager) -> Option<ResolvedLocation> {
100 match self.location.as_ref()? {
101 LogicalFrameLocation::Assembly(location) => {
102 resolve_assembly_location(source_manager, location)
103 }
104 LogicalFrameLocation::Resolved(resolved) => Some(resolved.clone()),
105 }
106 }
107}
108
109pub fn inline_frames_for_operation<'a>(
114 current: Option<(&PackageDebugInfo, DebugSourceNodeId, u32)>,
115 inherited: impl IntoIterator<Item = &'a SourceInlineCallContext>,
116) -> Vec<InlineCallFrame> {
117 let mut frames = Vec::new();
118 if let Some((debug_info, source_node, op_idx)) = current {
119 append_inline_frames(
120 &mut frames,
121 debug_info,
122 debug_info.inline_calls_for_operation(source_node, op_idx),
123 );
124 }
125 for context in inherited {
126 append_inline_frames(&mut frames, context.debug_info(), context.inline_calls());
127 }
128 frames
129}
130
131fn append_inline_frames<'a>(
132 frames: &mut Vec<InlineCallFrame>,
133 debug_info: &PackageDebugInfo,
134 rows: impl IntoIterator<Item = &'a DebugSourceInlineCall>,
135) {
136 frames.extend(rows.into_iter().filter_map(|row| {
137 let function = debug_info.get_function(row.callee_idx)?;
138 let name = debug_info.get_string(function.name_idx)?;
139 let call_site = debug_info.get_location(row.loc_idx)?;
140 Some(InlineCallFrame { name, call_site })
141 }));
142}
143
144#[derive(Debug, Clone)]
145struct SpanContext {
146 frame_index: usize,
147 location: Option<Location>,
148}
149
150pub struct CallStack {
151 events: Arc<RwLock<BTreeMap<RowIndex, Event>>>,
152 contexts: BTreeSet<Arc<str>>,
153 frames: Vec<CallFrame>,
154 block_stack: Vec<Option<SpanContext>>,
155}
156impl CallStack {
157 pub fn new(events: Arc<RwLock<BTreeMap<RowIndex, Event>>>) -> Self {
158 Self {
159 events,
160 contexts: BTreeSet::default(),
161 frames: vec![],
162 block_stack: vec![],
163 }
164 }
165
166 #[cfg(feature = "dap")]
168 pub fn from_remote_frames(frames: Vec<CallFrame>) -> Self {
169 Self {
170 events: Arc::new(Default::default()),
171 contexts: BTreeSet::default(),
172 frames,
173 block_stack: vec![],
174 }
175 }
176
177 pub fn stacktrace<'a>(
178 &'a self,
179 recent: &'a VecDeque<Operation>,
180 source_manager: &'a dyn SourceManager,
181 ) -> StackTrace<'a> {
182 StackTrace::new(self, recent, source_manager)
183 }
184
185 pub fn current_frame(&self) -> Option<&CallFrame> {
186 self.frames.last()
187 }
188
189 pub fn current_frame_mut(&mut self) -> Option<&mut CallFrame> {
190 self.frames.last_mut()
191 }
192
193 pub fn frames(&self) -> &[CallFrame] {
194 self.frames.as_slice()
195 }
196
197 pub fn logical_frames(&self, strip_prefix: &str) -> Vec<LogicalStackFrame> {
198 let mut logical = Vec::new();
199 for (physical_index, frame) in self.frames.iter().enumerate() {
200 let current_location = frame.last_logical_location();
201 let location = frame
202 .inline_frames
203 .last()
204 .map(|inline| LogicalFrameLocation::Assembly(inline.call_site.clone()))
205 .or_else(|| current_location.clone());
206 logical.push(LogicalStackFrame {
207 name: frame.procedure(strip_prefix).unwrap_or_else(|| Arc::from("<unknown>")),
208 kind: LogicalFrameKind::Physical,
209 location,
210 physical_index,
211 });
212
213 for inline_index in (0..frame.inline_frames.len()).rev() {
214 let inline = &frame.inline_frames[inline_index];
215 let location = if inline_index == 0 {
216 current_location.clone()
217 } else {
218 Some(LogicalFrameLocation::Assembly(
219 frame.inline_frames[inline_index - 1].call_site.clone(),
220 ))
221 };
222 logical.push(LogicalStackFrame {
223 name: Arc::from(inline.display_name().into_boxed_str()),
224 kind: LogicalFrameKind::Inline,
225 location,
226 physical_index,
227 });
228 }
229 }
230 logical
231 }
232
233 pub fn next(&mut self, info: &StepInfo<'_>) -> Option<CallFrame> {
237 let procedure = info.asmop.map(|op| self.cache_procedure_name(op.context_name()));
238
239 let event = {
240 let mut events = self.events.write();
241 match events.first_key_value() {
242 Some((clk, _)) if *clk <= info.clk => events.pop_first().map(|(_, event)| event),
243 _ => None,
244 }
245 };
246 log::trace!("handling {:?}/{:?} at cycle {}: {:?}", info.control, info.op, info.clk, event);
247 let is_frame_start = event.as_ref().is_some_and(|event| event.is_frame_start());
248 let is_frame_end = event.as_ref().is_some_and(|event| event.is_frame_end());
249 let popped_frame = self.handle_event(event, procedure.clone(), info.op, info.asmop);
250
251 match info.control {
252 Some(ControlFlowOp::Span) => {
253 if let Some(asmop) = info.asmop {
254 log::debug!("{asmop:#?}");
255 self.block_stack.push(Some(SpanContext {
256 frame_index: self.frames.len().saturating_sub(1),
257 location: asmop.location().cloned(),
258 }));
259 } else {
260 self.block_stack.push(None);
261 }
262 }
263 Some(ControlFlowOp::Join | ControlFlowOp::Split) => {
264 self.block_stack.push(None);
265 }
266 Some(ControlFlowOp::End) => {
267 self.block_stack.pop();
268 }
269 Some(ControlFlowOp::Respan) | None => {}
270 }
271
272 if !is_frame_end {
273 if self.frames.is_empty() {
274 self.frames.push(CallFrame::new(procedure.clone()));
275 }
276 self.frames.last_mut().unwrap().inline_frames = info.inline_frames.to_vec();
277 self.update_current_procedure(procedure.clone());
278 }
279
280 if is_frame_start || is_frame_end {
281 return popped_frame;
282 }
283
284 let Some(op) = info.op else {
285 return popped_frame;
286 };
287
288 let (procedure, asmop) = match procedure {
291 proc @ Some(_) => (proc, info.asmop.map(Cow::Borrowed)),
292 None => match self.block_stack.last() {
293 Some(Some(span_ctx)) => {
294 let proc =
295 self.frames.get(span_ctx.frame_index).and_then(|f| f.procedure.clone());
296 let asmop_cow = info.asmop.map(Cow::Borrowed).or_else(|| {
297 let context_name = proc.as_deref().unwrap_or("<unknown>").to_string();
298 let raw_asmop = AssemblyOp::new(
299 span_ctx.location.clone(),
300 context_name,
301 1,
302 op.to_string(),
303 );
304 Some(Cow::Owned(raw_asmop))
305 });
306 (proc, asmop_cow)
307 }
308 _ => (None, info.asmop.map(Cow::Borrowed)),
309 },
310 };
311
312 let procedure = procedure.or_else(|| self.frames.last().and_then(|f| f.procedure.clone()));
315
316 self.update_current_procedure(procedure);
319 let current_frame = self.frames.last_mut().unwrap();
320
321 if !matches!(op, Operation::Noop) {
323 let cycle_idx = info.asmop.map(|a| a.num_cycles()).unwrap_or(1);
324 current_frame.push(op, cycle_idx, asmop.as_deref());
325 }
326
327 popped_frame
328 }
329
330 fn update_current_procedure(&mut self, procedure: Option<Arc<str>>) {
331 let context_initialized = self
332 .frames
333 .last_mut()
334 .is_some_and(|frame| frame.update_procedure(procedure.clone()));
335 let num_frames = self.frames.len();
336 if context_initialized && num_frames > 1 {
337 let caller_frame = &mut self.frames[num_frames - 2];
338 if let Some(OpDetail::Exec { callee }) = caller_frame.context.back_mut()
339 && callee.is_none()
340 {
341 *callee = procedure;
342 }
343 }
344 }
345
346 fn cache_procedure_name(&mut self, context_name: &str) -> Arc<str> {
348 match self.contexts.get(context_name) {
349 Some(name) => Arc::clone(name),
350 None => {
351 let name = Arc::from(context_name.to_string().into_boxed_str());
352 self.contexts.insert(Arc::clone(&name));
353 name
354 }
355 }
356 }
357
358 fn handle_event(
359 &mut self,
360 event: Option<Event>,
361 procedure: Option<Arc<str>>,
362 op: Option<Operation>,
363 asmop: Option<&AssemblyOp>,
364 ) -> Option<CallFrame> {
365 match event? {
367 Event::FrameStart => {
368 if let Some(current_frame) = self.frames.last_mut() {
370 current_frame.push_exec(procedure.clone());
371 }
372 let mut frame = CallFrame::new(procedure);
374 if let Some(op) = op {
375 frame.push(op, 0, asmop);
376 }
377 self.frames.push(frame);
378 }
379 Event::Unknown(code) => log::debug!("unknown trace event: {code}"),
380 Event::FrameEnd => {
381 return self.frames.pop();
382 }
383 _ => (),
384 }
385 None
386 }
387}
388
389pub struct CallFrame {
390 procedure: Option<Arc<str>>,
391 context: VecDeque<OpDetail>,
392 display_name: OnceCell<Arc<str>>,
393 finishing: bool,
394 inline_frames: Vec<InlineCallFrame>,
395}
396impl CallFrame {
397 pub fn new(procedure: Option<Arc<str>>) -> Self {
398 Self {
399 procedure,
400 context: Default::default(),
401 display_name: Default::default(),
402 finishing: false,
403 inline_frames: Vec::new(),
404 }
405 }
406
407 #[cfg(feature = "dap")]
413 pub fn from_remote(procedure: Option<Arc<str>>, resolved: Option<ResolvedLocation>) -> Self {
414 let mut context = VecDeque::new();
415 if let Some(loc) = resolved {
416 let cell = OnceCell::new();
417 cell.set(Some(loc)).ok();
418 context.push_back(OpDetail::Full {
419 op: miden_processor::operation::Operation::Noop,
420 location: None,
421 resolved: cell,
422 });
423 }
424 Self {
425 procedure,
426 context,
427 display_name: Default::default(),
428 finishing: false,
429 inline_frames: Vec::new(),
430 }
431 }
432
433 pub fn procedure(&self, strip_prefix: &str) -> Option<Arc<str>> {
434 self.procedure.as_ref()?;
435 let name = self.display_name.get_or_init(|| {
436 let name = self.procedure.as_deref().unwrap();
437 let name = match name.split_once("::") {
438 Some((module, rest)) if module == strip_prefix => demangle(rest),
439 _ => demangle(name),
440 };
441 Arc::<str>::from(name.into_boxed_str())
442 });
443 Some(Arc::clone(name))
444 }
445
446 fn update_procedure(&mut self, procedure: Option<Arc<str>>) -> bool {
450 let Some(procedure) = procedure else {
451 return false;
452 };
453 if self.procedure.as_ref() == Some(&procedure) {
454 return false;
455 }
456
457 let initialized = self.procedure.is_none();
458 self.procedure = Some(procedure);
459 self.display_name.take();
460 initialized
461 }
462
463 pub fn push_exec(&mut self, callee: Option<Arc<str>>) {
464 if self.context.len() == 5 {
465 self.context.pop_front();
466 }
467
468 self.context.push_back(OpDetail::Exec { callee });
469 }
470
471 pub fn push(&mut self, opcode: Operation, cycle_idx: u8, op: Option<&AssemblyOp>) {
472 if cycle_idx > 1 {
473 let skip = self.context.back().map(|detail| matches!(detail, OpDetail::Full { op, .. } | OpDetail::Basic { op } if op == &opcode)).unwrap_or(false);
475 if skip {
476 return;
477 }
478 }
479
480 if self.context.len() == 5 {
481 self.context.pop_front();
482 }
483
484 match op {
485 Some(op) => {
486 let location = op.location().cloned();
487 self.context.push_back(OpDetail::Full {
488 op: opcode,
489 location,
490 resolved: Default::default(),
491 });
492 }
493 None => {
494 if let Some(loc) = self.context.back().map(|op| op.location().cloned()) {
497 self.context.push_back(OpDetail::Full {
498 op: opcode,
499 location: loc,
500 resolved: Default::default(),
501 });
502 } else {
503 self.context.push_back(OpDetail::Basic { op: opcode });
504 }
505 }
506 }
507 }
508
509 pub fn last_location(&self) -> Option<&Location> {
510 self.context.iter().rev().find_map(OpDetail::location)
511 }
512
513 fn last_logical_location(&self) -> Option<LogicalFrameLocation> {
514 self.context.iter().rev().find_map(|detail| {
515 detail
516 .location()
517 .cloned()
518 .map(LogicalFrameLocation::Assembly)
519 .or_else(|| detail.cached_resolved().cloned().map(LogicalFrameLocation::Resolved))
520 })
521 }
522
523 pub fn last_resolved(&self, source_manager: &dyn SourceManager) -> Option<&ResolvedLocation> {
524 for op in self.context.iter().rev() {
527 if let Some(resolved) = op.resolve(source_manager) {
528 return Some(resolved);
529 }
530 }
531 None
532 }
533
534 pub fn recent(&self) -> &VecDeque<OpDetail> {
535 &self.context
536 }
537
538 #[inline(always)]
539 pub fn should_break_on_exit(&self) -> bool {
540 self.finishing
541 }
542
543 #[inline(always)]
544 pub fn break_on_exit(&mut self) {
545 self.finishing = true;
546 }
547}
548
549#[derive(Debug, Clone)]
550pub enum OpDetail {
551 Full {
552 op: Operation,
553 location: Option<Location>,
554 resolved: OnceCell<Option<ResolvedLocation>>,
555 },
556 Exec {
557 callee: Option<Arc<str>>,
558 },
559 Basic {
560 op: Operation,
561 },
562}
563impl OpDetail {
564 pub fn callee(&self, strip_prefix: &str) -> Option<Box<str>> {
565 match self {
566 Self::Exec { callee: None } => Some(Box::from("<unknown>")),
567 Self::Exec {
568 callee: Some(callee),
569 } => {
570 let name = match callee.split_once("::") {
571 Some((module, rest)) if module == strip_prefix => demangle(rest),
572 _ => demangle(callee),
573 };
574 Some(name.into_boxed_str())
575 }
576 _ => None,
577 }
578 }
579
580 pub fn display(&self) -> String {
581 match self {
582 Self::Full { op, .. } | Self::Basic { op } => format!("{op}"),
583 Self::Exec {
584 callee: Some(callee),
585 } => format!("exec.{callee}"),
586 Self::Exec { callee: None } => "exec.<unavailable>".to_string(),
587 }
588 }
589
590 pub fn opcode(&self) -> Operation {
591 match self {
592 Self::Full { op, .. } | Self::Basic { op } => *op,
593 Self::Exec { .. } => panic!("no opcode associated with execs"),
594 }
595 }
596
597 pub fn location(&self) -> Option<&Location> {
598 match self {
599 Self::Full { location, .. } => location.as_ref(),
600 Self::Basic { .. } | Self::Exec { .. } => None,
601 }
602 }
603
604 pub fn resolve(&self, source_manager: &dyn SourceManager) -> Option<&ResolvedLocation> {
605 match self {
606 Self::Full {
607 location, resolved, ..
608 } => {
609 if let Some(cached) = resolved.get() {
610 return cached.as_ref();
611 }
612 let loc = location.as_ref()?;
613 resolved
614 .get_or_init(|| {
615 let source_file = resolve_source_file_for_location(source_manager, loc)?;
616 let span = SourceSpan::new(source_file.id(), loc.start..loc.end);
617 let file_line_col = source_file.location(span);
618 Some(ResolvedLocation {
619 source_file,
620 line: file_line_col.line.to_u32(),
621 col: file_line_col.column.to_u32(),
622 span,
623 })
624 })
625 .as_ref()
626 }
627 _ => None,
628 }
629 }
630
631 fn cached_resolved(&self) -> Option<&ResolvedLocation> {
632 match self {
633 Self::Full { resolved, .. } => resolved.get().and_then(Option::as_ref),
634 Self::Exec { .. } | Self::Basic { .. } => None,
635 }
636 }
637}
638
639#[cfg(feature = "std")]
645pub fn resolve_source_file_for_location(
646 source_manager: &dyn SourceManager,
647 location: &Location,
648) -> Option<Arc<SourceFile>> {
649 use miden_assembly_syntax::debuginfo::SourceManagerExt;
650 source_manager.get_by_uri(location.uri()).or_else(|| {
651 resolve_source_path(location.uri()).and_then(|path| source_manager.load_file(&path).ok())
652 })
653}
654
655#[cfg(not(feature = "std"))]
656pub fn resolve_source_file_for_location(
657 source_manager: &dyn SourceManager,
658 location: &Location,
659) -> Option<Arc<SourceFile>> {
660 source_manager.get_by_uri(location.uri())
661}
662
663#[cfg(feature = "std")]
668pub fn resolve_source_path(uri: &Uri) -> Option<PathBuf> {
669 let path = match uri.scheme() {
670 None | Some("file") => uri.to_path()?,
671 Some(_) => return None,
672 };
673
674 fn existing_path(path: &Path) -> Option<PathBuf> {
675 path.exists()
676 .then(|| path.canonicalize().unwrap_or_else(|_| path.to_path_buf()))
677 }
678
679 existing_path(&path).or_else(|| {
680 if path.is_relative() {
681 std::env::current_dir().ok().and_then(|cwd| existing_path(&cwd.join(path)))
682 } else {
683 None
684 }
685 })
686}
687
688#[cfg(feature = "std")]
690pub fn resolve_location_from_filesystem(location: &Location) -> Option<(PathBuf, u32)> {
691 let path = resolve_source_path(location.uri())?;
692 let bytes = std::fs::read(&path).ok()?;
693 let start = location.start.to_usize().min(bytes.len());
694 let line = bytes[..start].iter().filter(|byte| **byte == b'\n').count() as u32 + 1;
695 Some((path, line))
696}
697
698pub fn is_internal_source_uri(uri: &Uri) -> bool {
700 let path = uri.as_str().replace('\\', "/");
701 path.contains("/codegen/masm/intrinsics/") || path.contains("/rustlib/src/rust/library/")
702}
703
704#[derive(Debug, Clone)]
705pub struct ResolvedLocation {
706 pub source_file: Arc<SourceFile>,
707 pub line: u32,
709 pub col: u32,
710 pub span: SourceSpan,
711}
712impl fmt::Display for ResolvedLocation {
713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714 write!(f, "{}:{}:{}", self.source_file.uri().as_str(), self.line, self.col)
715 }
716}
717
718pub struct CurrentFrame {
719 pub procedure: Option<Arc<str>>,
720 pub location: Option<ResolvedLocation>,
721}
722
723pub struct StackTrace<'a> {
724 callstack: &'a CallStack,
725 recent: &'a VecDeque<Operation>,
726 source_manager: &'a dyn SourceManager,
727 current_frame: Option<CurrentFrame>,
728}
729
730impl<'a> StackTrace<'a> {
731 pub fn new(
732 callstack: &'a CallStack,
733 recent: &'a VecDeque<Operation>,
734 source_manager: &'a dyn SourceManager,
735 ) -> Self {
736 let current_frame = callstack.logical_frames("").last().map(|frame| {
737 let location = frame.resolved(source_manager);
738 let procedure = Some(Arc::from(frame.display_name().into_boxed_str()));
739 CurrentFrame {
740 procedure,
741 location,
742 }
743 });
744 Self {
745 callstack,
746 recent,
747 source_manager,
748 current_frame,
749 }
750 }
751
752 pub fn current_frame(&self) -> Option<&CurrentFrame> {
753 self.current_frame.as_ref()
754 }
755}
756
757impl fmt::Display for StackTrace<'_> {
758 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
759 use core::fmt::Write;
760
761 let frames = self.callstack.logical_frames("");
762 let num_frames = frames.len();
763
764 writeln!(f, "\nStack Trace:")?;
765
766 for (i, frame) in frames.iter().enumerate() {
767 let is_top = i + 1 == num_frames;
768 let name = frame.display_name();
769 if is_top {
770 write!(f, " `-> {name}")?;
771 } else {
772 write!(f, " |-> {name}")?;
773 }
774 if let Some(resolved) = frame.resolved(self.source_manager) {
775 write!(f, " in {resolved}")?;
776 } else {
777 write!(f, " in <unavailable>")?;
778 }
779 if is_top {
780 let physical_frame = &self.callstack.frames[frame.physical_index()];
781 let context_size = physical_frame.context.len();
783 writeln!(f, ":\n\nLast {context_size} Instructions (of current frame):")?;
784 for (i, op) in physical_frame.context.iter().enumerate() {
785 let is_last = i + 1 == context_size;
786 if let Some(callee) = op.callee("") {
787 write!(f, " | exec.{callee}")?;
788 } else {
789 write!(f, " | {}", op.opcode())?;
790 }
791 if is_last {
792 writeln!(f, "\n `-> <error occurred here>")?;
793 } else {
794 f.write_char('\n')?;
795 }
796 }
797
798 let context_size = self.recent.len();
799 writeln!(f, "\n\nLast {context_size} Instructions (any frame):")?;
800 for (i, op) in self.recent.iter().enumerate() {
801 let is_last = i + 1 == context_size;
802 if is_last {
803 writeln!(f, " | {}", op)?;
804 writeln!(f, " `-> <error occurred here>")?;
805 } else {
806 writeln!(f, " | {}", op)?;
807 }
808 }
809 } else {
810 f.write_char('\n')?;
811 }
812 }
813
814 Ok(())
815 }
816}
817
818fn resolve_assembly_location(
819 source_manager: &dyn SourceManager,
820 location: &Location,
821) -> Option<ResolvedLocation> {
822 let source_file = resolve_source_file_for_location(source_manager, location)?;
823 let span = SourceSpan::new(source_file.id(), location.start..location.end);
824 let file_line_col = source_file.location(span);
825 Some(ResolvedLocation {
826 source_file,
827 line: file_line_col.line.to_u32(),
828 col: file_line_col.column.to_u32(),
829 span,
830 })
831}
832
833#[cfg(feature = "std")]
834fn demangle(name: &str) -> String {
835 let mut input = name.as_bytes();
836 let mut demangled = Vec::with_capacity(input.len() * 2);
837 rustc_demangle::demangle_stream(&mut input, &mut demangled, false)
838 .expect("failed to write demangled identifier");
839 String::from_utf8(demangled).expect("demangled identifier contains invalid utf-8")
840}
841
842#[cfg(not(feature = "std"))]
843fn demangle(name: &str) -> String {
844 rustc_demangle::demangle(name).to_string()
845}
846
847#[cfg(test)]
848mod tests;