Skip to content

Commit 62543c9

Browse files
committed
add new version of backtrace api using flags=1
1 parent a284d4f commit 62543c9

9 files changed

+264
-60
lines changed

src/shims/backtrace.rs

Lines changed: 159 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,49 @@
11
use crate::*;
22
use rustc_ast::ast::Mutability;
33
use rustc_middle::ty::layout::LayoutOf as _;
4-
use rustc_middle::ty::{self, TypeAndMut};
5-
use rustc_span::{BytePos, Symbol};
4+
use rustc_middle::ty::{self, Instance, TypeAndMut};
5+
use rustc_span::{BytePos, Loc, Symbol};
66
use rustc_target::{abi::Size, spec::abi::Abi};
77
use std::convert::TryInto as _;
88

99
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1010
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
11-
fn handle_miri_get_backtrace(
11+
fn handle_miri_backtrace_size(
1212
&mut self,
1313
abi: Abi,
1414
link_name: Symbol,
1515
args: &[OpTy<'tcx, Tag>],
1616
dest: &PlaceTy<'tcx, Tag>,
1717
) -> InterpResult<'tcx> {
1818
let this = self.eval_context_mut();
19-
let tcx = this.tcx;
2019
let &[ref flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
2120

2221
let flags = this.read_scalar(flags)?.to_u64()?;
23-
if flags != 0 {
24-
throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags);
22+
if flags != 1 {
23+
throw_unsup_format!("unknown `miri_backtrace_size` flags {}", flags);
2524
}
2625

26+
let frame_count = this.active_thread_stack().len();
27+
28+
this.write_scalar(Scalar::from_machine_usize(frame_count.try_into().unwrap(), this), dest)
29+
}
30+
31+
fn handle_miri_get_backtrace(
32+
&mut self,
33+
abi: Abi,
34+
link_name: Symbol,
35+
args: &[OpTy<'tcx, Tag>],
36+
dest: &PlaceTy<'tcx, Tag>,
37+
) -> InterpResult<'tcx> {
38+
let this = self.eval_context_mut();
39+
let tcx = this.tcx;
40+
41+
let flags = if let Some(flags_op) = args.get(0) {
42+
this.read_scalar(flags_op)?.to_u64()?
43+
} else {
44+
throw_ub_format!("expected at least 1 argument")
45+
};
46+
2747
let mut data = Vec::new();
2848
for frame in this.active_thread_stack().iter().rev() {
2949
let mut span = frame.current_span();
@@ -49,46 +69,69 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4969
})
5070
.collect();
5171

52-
let len = ptrs.len();
72+
let len = ptrs.len().try_into().unwrap();
5373

5474
let ptr_ty = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
5575

56-
let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap());
76+
let array_ty = this.layout_of(tcx.mk_array(ptr_ty, len)).unwrap();
5777

58-
// Write pointers into array
59-
let alloc =
60-
this.allocate(this.layout_of(array_ty).unwrap(), MiriMemoryKind::Rust.into())?;
61-
for (i, ptr) in ptrs.into_iter().enumerate() {
62-
let place = this.mplace_index(&alloc, i as u64)?;
63-
this.write_pointer(ptr, &place.into())?;
64-
}
78+
match flags {
79+
// storage for pointers is allocated by miri
80+
// deallocating the slice is undefined behavior with a custom global allocator
81+
0 => {
82+
let &[_] = this.check_shim(abi, Abi::Rust, link_name, args)?;
83+
84+
let alloc = this.allocate(array_ty, MiriMemoryKind::Rust.into())?;
85+
86+
// Write pointers into array
87+
for (i, ptr) in ptrs.into_iter().enumerate() {
88+
let place = this.mplace_index(&alloc, i as u64)?;
89+
90+
this.write_pointer(ptr, &place.into())?;
91+
}
92+
93+
this.write_immediate(
94+
Immediate::new_slice(Scalar::from_maybe_pointer(alloc.ptr, this), len, this),
95+
dest,
96+
)?;
97+
}
98+
// storage for pointers is allocated by the caller
99+
1 => {
100+
let &[_, ref buf] = this.check_shim(abi, Abi::Rust, link_name, args)?;
101+
102+
this.memory.check_ptr_access_align(
103+
this.read_pointer(buf)?,
104+
array_ty.size,
105+
array_ty.align.abi,
106+
CheckInAllocMsg::MemoryAccessTest,
107+
)?;
108+
109+
let ptr_layout = this.layout_of(ptr_ty)?;
110+
111+
for (i, ptr) in ptrs.into_iter().enumerate() {
112+
let offset = ptr_layout.size * i.try_into().unwrap();
113+
114+
let op_place = this.deref_operand(buf)?.offset(
115+
offset,
116+
MemPlaceMeta::None,
117+
ptr_layout,
118+
this,
119+
)?;
120+
121+
this.write_pointer(ptr, &op_place.into())?;
122+
}
123+
}
124+
_ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags),
125+
};
65126

66-
this.write_immediate(
67-
Immediate::new_slice(
68-
Scalar::from_maybe_pointer(alloc.ptr, this),
69-
len.try_into().unwrap(),
70-
this,
71-
),
72-
dest,
73-
)?;
74127
Ok(())
75128
}
76129

77-
fn handle_miri_resolve_frame(
130+
fn resolve_frame_pointer(
78131
&mut self,
79-
abi: Abi,
80-
link_name: Symbol,
81-
args: &[OpTy<'tcx, Tag>],
82-
dest: &PlaceTy<'tcx, Tag>,
83-
) -> InterpResult<'tcx> {
132+
ptr: &OpTy<'tcx, Tag>,
133+
) -> InterpResult<'tcx, (Instance<'tcx>, Loc)> {
84134
let this = self.eval_context_mut();
85-
let tcx = this.tcx;
86-
let &[ref ptr, ref flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
87-
88-
let flags = this.read_scalar(flags)?.to_u64()?;
89-
if flags != 0 {
90-
throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags);
91-
}
92135

93136
let ptr = this.read_pointer(ptr)?;
94137
// Take apart the pointer, we need its pieces.
@@ -101,6 +144,26 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
101144
throw_ub_format!("expected function pointer, found {:?}", ptr);
102145
};
103146

147+
let lo =
148+
this.tcx.sess.source_map().lookup_char_pos(BytePos(offset.bytes().try_into().unwrap()));
149+
150+
Ok((fn_instance, lo))
151+
}
152+
153+
fn handle_miri_resolve_frame(
154+
&mut self,
155+
abi: Abi,
156+
link_name: Symbol,
157+
args: &[OpTy<'tcx, Tag>],
158+
dest: &PlaceTy<'tcx, Tag>,
159+
) -> InterpResult<'tcx> {
160+
let this = self.eval_context_mut();
161+
let &[ref ptr, ref flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
162+
163+
let flags = this.read_scalar(flags)?.to_u64()?;
164+
165+
let (fn_instance, lo) = this.resolve_frame_pointer(ptr)?;
166+
104167
// Reconstruct the original function pointer,
105168
// which we pass to user code.
106169
let fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(fn_instance));
@@ -115,23 +178,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
115178
);
116179
}
117180

118-
let pos = BytePos(offset.bytes().try_into().unwrap());
119181
let name = fn_instance.to_string();
120182

121-
let lo = tcx.sess.source_map().lookup_char_pos(pos);
122-
123183
let filename = lo.file.name.prefer_remapped().to_string();
124184
let lineno: u32 = lo.line as u32;
125185
// `lo.col` is 0-based - add 1 to make it 1-based for the caller.
126186
let colno: u32 = lo.col.0 as u32 + 1;
127187

128-
// These are "mutable" allocations as we consider them to be owned by the callee.
129-
let name_alloc = this.allocate_str(&name, MiriMemoryKind::Rust.into(), Mutability::Mut);
130-
let filename_alloc =
131-
this.allocate_str(&filename, MiriMemoryKind::Rust.into(), Mutability::Mut);
132-
let lineno_alloc = Scalar::from_u32(lineno);
133-
let colno_alloc = Scalar::from_u32(colno);
134-
135188
let dest = this.force_allocation(dest)?;
136189
if let ty::Adt(adt, _) = dest.layout.ty.kind() {
137190
if !adt.repr.c() {
@@ -141,10 +194,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
141194
}
142195
}
143196

144-
this.write_immediate(name_alloc.to_ref(this), &this.mplace_field(&dest, 0)?.into())?;
145-
this.write_immediate(filename_alloc.to_ref(this), &this.mplace_field(&dest, 1)?.into())?;
146-
this.write_scalar(lineno_alloc, &this.mplace_field(&dest, 2)?.into())?;
147-
this.write_scalar(colno_alloc, &this.mplace_field(&dest, 3)?.into())?;
197+
match flags {
198+
0 => {
199+
// These are "mutable" allocations as we consider them to be owned by the callee.
200+
let name_alloc =
201+
this.allocate_str(&name, MiriMemoryKind::Rust.into(), Mutability::Mut);
202+
let filename_alloc =
203+
this.allocate_str(&filename, MiriMemoryKind::Rust.into(), Mutability::Mut);
204+
205+
this.write_immediate(
206+
name_alloc.to_ref(this),
207+
&this.mplace_field(&dest, 0)?.into(),
208+
)?;
209+
this.write_immediate(
210+
filename_alloc.to_ref(this),
211+
&this.mplace_field(&dest, 1)?.into(),
212+
)?;
213+
}
214+
1 => {
215+
this.write_scalar(
216+
Scalar::from_machine_usize(name.len().try_into().unwrap(), this),
217+
&this.mplace_field(&dest, 0)?.into(),
218+
)?;
219+
this.write_scalar(
220+
Scalar::from_machine_usize(filename.len().try_into().unwrap(), this),
221+
&this.mplace_field(&dest, 1)?.into(),
222+
)?;
223+
}
224+
_ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags),
225+
}
226+
227+
this.write_scalar(Scalar::from_u32(lineno), &this.mplace_field(&dest, 2)?.into())?;
228+
this.write_scalar(Scalar::from_u32(colno), &this.mplace_field(&dest, 3)?.into())?;
148229

149230
// Support a 4-field struct for now - this is deprecated
150231
// and slated for removal.
@@ -154,4 +235,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
154235

155236
Ok(())
156237
}
238+
239+
fn handle_miri_resolve_frame_names(
240+
&mut self,
241+
abi: Abi,
242+
link_name: Symbol,
243+
args: &[OpTy<'tcx, Tag>],
244+
dest: &PlaceTy<'tcx, Tag>,
245+
) -> InterpResult<'tcx> {
246+
let this = self.eval_context_mut();
247+
248+
let &[ref ptr, ref flags, ref name_ptr, ref filename_ptr] =
249+
this.check_shim(abi, Abi::Rust, link_name, args)?;
250+
251+
let flags = this.read_scalar(flags)?.to_u64()?;
252+
if flags != 1 {
253+
throw_unsup_format!("unknown `miri_backtrace_size` flags {}", flags);
254+
}
255+
256+
let (fn_instance, lo) = this.resolve_frame_pointer(ptr)?;
257+
258+
let name = fn_instance.to_string();
259+
let filename = lo.file.name.prefer_remapped().to_string();
260+
261+
this.memory.write_bytes(this.read_pointer(name_ptr)?, name.bytes())?;
262+
this.memory.write_bytes(this.read_pointer(filename_ptr)?, filename.bytes())?;
263+
264+
Ok(())
265+
}
157266
}

src/shims/foreign_items.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
380380
this.machine.static_roots.push(alloc_id);
381381
}
382382

383+
"miri_backtrace_size" => {
384+
this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
385+
}
386+
383387
// Obtains a Miri backtrace. See the README for details.
384388
"miri_get_backtrace" => {
385389
// `check_shim` happens inside `handle_miri_get_backtrace`.
@@ -392,6 +396,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
392396
this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
393397
}
394398

399+
"miri_resolve_frame_names" => {
400+
this.handle_miri_resolve_frame_names(abi, link_name, args, dest)?;
401+
}
395402

396403
// Standard C allocation
397404
"malloc" => {
File renamed without changes.

tests/run-pass/backtrace-api.stderr renamed to tests/run-pass/backtrace-api-v0.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
$DIR/backtrace-api.rs:13:59 (func_d)
2-
$DIR/backtrace-api.rs:12:50 (func_c)
3-
$DIR/backtrace-api.rs:6:53 (func_b)
4-
$DIR/backtrace-api.rs:5:50 (func_a)
5-
$DIR/backtrace-api.rs:17:18 (main)
1+
$DIR/backtrace-api-v0.rs:13:59 (func_d)
2+
$DIR/backtrace-api-v0.rs:12:50 (func_c)
3+
$DIR/backtrace-api-v0.rs:6:53 (func_b)
4+
$DIR/backtrace-api-v0.rs:5:50 (func_a)
5+
$DIR/backtrace-api-v0.rs:17:18 (main)
66
RUSTLIB/core/src/ops/function.rs:LL:COL (<fn() as std::ops::FnOnce<()>>::call_once - shim(fn()))
77
RUSTLIB/std/src/sys_common/backtrace.rs:LL:COL (std::sys_common::backtrace::__rust_begin_short_backtrace)
88
RUSTLIB/std/src/rt.rs:LL:COL (std::rt::lang_start::{closure#0})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$DIR/backtrace-api-v0.rs:13:59 (func_d)
2+
$DIR/backtrace-api-v0.rs:12:50 (func_c)
3+
$DIR/backtrace-api-v0.rs:6:53 (func_b::<u8>)
4+
$DIR/backtrace-api-v0.rs:5:50 (func_a)
5+
$DIR/backtrace-api-v0.rs:17:18 (main)

tests/run-pass/backtrace-api-v1.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// normalize-stderr-test ".*/(rust[^/]*|checkout)/library/" -> "RUSTLIB/"
2+
// normalize-stderr-test "RUSTLIB/(.*):\d+:\d+ "-> "RUSTLIB/$1:LL:COL "
3+
// normalize-stderr-test "::<.*>" -> ""
4+
5+
#[inline(never)] fn func_a() -> Box<[*mut ()]> { func_b::<u8>() }
6+
#[inline(never)] fn func_b<T>() -> Box<[*mut ()]> { func_c() }
7+
8+
macro_rules! invoke_func_d {
9+
() => { func_d() }
10+
}
11+
12+
#[inline(never)] fn func_c() -> Box<[*mut ()]> { invoke_func_d!() }
13+
#[inline(never)] fn func_d() -> Box<[*mut ()]> { unsafe { let count = miri_backtrace_size(1); let mut buf = vec![std::ptr::null_mut(); count]; miri_get_backtrace(1, buf.as_mut_ptr()); buf.into() } }
14+
15+
fn main() {
16+
let mut seen_main = false;
17+
let frames = func_a();
18+
for frame in frames.into_iter() {
19+
let miri_frame = unsafe { miri_resolve_frame(*frame, 1) };
20+
21+
let mut name = vec![0; miri_frame.name_len];
22+
let mut filename = vec![0; miri_frame.filename_len];
23+
24+
unsafe {
25+
miri_resolve_frame_names(*frame, 1, name.as_mut_ptr(), filename.as_mut_ptr());
26+
}
27+
28+
let name = String::from_utf8(name).unwrap();
29+
let filename = String::from_utf8(filename).unwrap();
30+
31+
if name == "func_a" {
32+
assert_eq!(func_a as *mut (), miri_frame.fn_ptr);
33+
}
34+
35+
// Print every frame to stderr.
36+
let out = format!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name);
37+
eprintln!("{}", out);
38+
// Print the 'main' frame (and everything before it) to stdout, skipping
39+
// the printing of internal (and possibly fragile) libstd frames.
40+
if !seen_main {
41+
println!("{}", out);
42+
seen_main = name == "main";
43+
}
44+
}
45+
}
46+
47+
// This goes at the bottom of the file so that we can change it
48+
// without disturbing line numbers of the functions in the backtrace.
49+
50+
extern "Rust" {
51+
fn miri_backtrace_size(flags: u64) -> usize;
52+
fn miri_get_backtrace(flags: u64, buf: *mut *mut ());
53+
fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
54+
fn miri_resolve_frame_names(ptr: *mut (), flags: u64, name_buf: *mut u8, filename_buf: *mut u8);
55+
}
56+
57+
#[derive(Debug)]
58+
#[repr(C)]
59+
struct MiriFrame {
60+
name_len: usize,
61+
filename_len: usize,
62+
lineno: u32,
63+
colno: u32,
64+
fn_ptr: *mut (),
65+
}

0 commit comments

Comments
 (0)