-
Notifications
You must be signed in to change notification settings - Fork 348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add API for capturing backtrace #1559
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
22f1eb0
Add API for capturing backtrace
Aaron1011 ae18659
Normalize line and column numbers from the sysroot
Aaron1011 ef43c5a
Use a 'flags' parameter instead of 'version'
Aaron1011 9fc384f
Print non-std frames to stdout in `backtrace-api` test
Aaron1011 b89f656
Move things around
Aaron1011 f756e3a
Explain encoding scheme
Aaron1011 e1bce19
Make some error messages lowercase
Aaron1011 11e2dbd
Update README
Aaron1011 dba7f13
Apply #[inline(never)] to functions that we want in the backtrace
Aaron1011 5571bcf
Require #[repr(C)] on MiriFrame
Aaron1011 b1837d0
fix typo
RalfJung 7fba3c2
Normalize out generic arguments in backtrace-api stderr
Aaron1011 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use crate::*; | ||
use helpers::check_arg_count; | ||
use rustc_middle::ty::{self, TypeAndMut}; | ||
use rustc_ast::ast::Mutability; | ||
use rustc_span::BytePos; | ||
use rustc_target::abi::Size; | ||
use std::convert::TryInto as _; | ||
use crate::rustc_target::abi::LayoutOf as _; | ||
|
||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} | ||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { | ||
|
||
fn handle_miri_get_backtrace( | ||
&mut self, | ||
args: &[OpTy<'tcx, Tag>], | ||
dest: PlaceTy<'tcx, Tag> | ||
) -> InterpResult<'tcx> { | ||
let this = self.eval_context_mut(); | ||
let tcx = this.tcx; | ||
let &[flags] = check_arg_count(args)?; | ||
|
||
let flags = this.read_scalar(flags)?.to_u64()?; | ||
if flags != 0 { | ||
throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags); | ||
} | ||
|
||
let mut data = Vec::new(); | ||
for frame in this.active_thread_stack().iter().rev() { | ||
data.push((frame.instance, frame.current_span().lo())); | ||
} | ||
|
||
let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| { | ||
// We represent a frame pointer by using the `span.lo` value | ||
// as an offset into the function's allocation. This gives us an | ||
// opaque pointer that we can return to user code, and allows us | ||
// to reconstruct the needed frame information in `handle_miri_resolve_frame`. | ||
// Note that we never actually read or write anything from/to this pointer - | ||
// all of the data is represented by the pointer value itself. | ||
let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); | ||
fn_ptr.offset = Size::from_bytes(pos.0); | ||
Scalar::Ptr(fn_ptr) | ||
}).collect(); | ||
|
||
let len = ptrs.len(); | ||
|
||
let ptr_ty = tcx.mk_ptr(TypeAndMut { | ||
ty: tcx.types.unit, | ||
mutbl: Mutability::Mut | ||
}); | ||
|
||
let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap()); | ||
|
||
// Write pointers into array | ||
let alloc = this.allocate(this.layout_of(array_ty).unwrap(), MiriMemoryKind::Rust.into()); | ||
for (i, ptr) in ptrs.into_iter().enumerate() { | ||
let place = this.mplace_index(alloc, i as u64)?; | ||
this.write_immediate_to_mplace(ptr.into(), place)?; | ||
} | ||
|
||
this.write_immediate(Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), dest)?; | ||
Ok(()) | ||
} | ||
|
||
fn handle_miri_resolve_frame( | ||
&mut self, | ||
args: &[OpTy<'tcx, Tag>], | ||
dest: PlaceTy<'tcx, Tag> | ||
) -> InterpResult<'tcx> { | ||
let this = self.eval_context_mut(); | ||
let tcx = this.tcx; | ||
let &[ptr, flags] = check_arg_count(args)?; | ||
|
||
let flags = this.read_scalar(flags)?.to_u64()?; | ||
if flags != 0 { | ||
throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags); | ||
} | ||
|
||
let ptr = match this.read_scalar(ptr)?.check_init()? { | ||
Scalar::Ptr(ptr) => ptr, | ||
Scalar::Raw { .. } => throw_ub_format!("expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) | ||
}; | ||
|
||
let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) { | ||
instance | ||
} else { | ||
throw_ub_format!("expected function pointer, found {:?}", ptr); | ||
}; | ||
|
||
if dest.layout.layout.fields.count() != 4 { | ||
throw_ub_format!("bad declaration of miri_resolve_frame - should return a struct with 4 fields"); | ||
} | ||
|
||
let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); | ||
let name = fn_instance.to_string(); | ||
|
||
let lo = tcx.sess.source_map().lookup_char_pos(pos); | ||
|
||
let filename = lo.file.name.to_string(); | ||
let lineno: u32 = lo.line as u32; | ||
// `lo.col` is 0-based - add 1 to make it 1-based for the caller. | ||
let colno: u32 = lo.col.0 as u32 + 1; | ||
|
||
let name_alloc = this.allocate_str(&name, MiriMemoryKind::Rust.into()); | ||
let filename_alloc = this.allocate_str(&filename, MiriMemoryKind::Rust.into()); | ||
let lineno_alloc = Scalar::from_u32(lineno); | ||
let colno_alloc = Scalar::from_u32(colno); | ||
|
||
let dest = this.force_allocation(dest)?; | ||
if let ty::Adt(adt, _) = dest.layout.ty.kind() { | ||
if !adt.repr.c() { | ||
throw_ub_format!("miri_resolve_frame must be declared with a `#[repr(C)]` return type"); | ||
} | ||
} | ||
|
||
this.write_immediate(name_alloc.to_ref(), this.mplace_field(dest, 0)?.into())?; | ||
this.write_immediate(filename_alloc.to_ref(), this.mplace_field(dest, 1)?.into())?; | ||
this.write_scalar(lineno_alloc, this.mplace_field(dest, 2)?.into())?; | ||
this.write_scalar(colno_alloc, this.mplace_field(dest, 3)?.into())?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
|
||
mod backtrace; | ||
pub mod foreign_items; | ||
pub mod intrinsics; | ||
pub mod posix; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
extern "Rust" { | ||
fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; | ||
fn miri_resolve_frame(ptr: *mut (), flags: u64); | ||
} | ||
|
||
fn main() { | ||
let frames = unsafe { miri_get_backtrace(0) }; | ||
for frame in frames.into_iter() { | ||
unsafe { | ||
miri_resolve_frame(*frame, 0); //~ ERROR Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 4 fields | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
extern "Rust" { | ||
fn miri_resolve_frame(ptr: *mut (), flags: u64); | ||
} | ||
|
||
fn main() { | ||
unsafe { | ||
miri_resolve_frame(0 as *mut _, 0); //~ ERROR Undefined Behavior: expected a pointer | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
extern "Rust" { | ||
fn miri_resolve_frame(ptr: *mut (), flags: u64); | ||
} | ||
|
||
fn main() { | ||
unsafe { | ||
miri_resolve_frame(0 as *mut _, 1); //~ ERROR unsupported operation: unknown `miri_resolve_frame` flags 1 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// normalize-stderr-test ".*rustlib" -> "RUSTLIB" | ||
Aaron1011 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// normalize-stderr-test "RUSTLIB/(.*):\d+:\d+ "-> "RUSTLIB/$1:LL:COL " | ||
// normalize-stderr-test "::<.*>" -> "" | ||
|
||
extern "Rust" { | ||
fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; | ||
fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame; | ||
} | ||
|
||
#[derive(Debug)] | ||
#[repr(C)] | ||
struct MiriFrame { | ||
name: Box<[u8]>, | ||
filename: Box<[u8]>, | ||
lineno: u32, | ||
colno: u32 | ||
} | ||
|
||
#[inline(never)] fn func_a() -> Box<[*mut ()]> { func_b::<u8>() } | ||
#[inline(never)] fn func_b<T>() -> Box<[*mut ()]> { func_c() } | ||
#[inline(never)] fn func_c() -> Box<[*mut ()]> { unsafe { miri_get_backtrace(0) } } | ||
|
||
fn main() { | ||
let mut seen_main = false; | ||
let frames = func_a(); | ||
for frame in frames.into_iter() { | ||
let miri_frame = unsafe { miri_resolve_frame(*frame, 0) }; | ||
let name = String::from_utf8(miri_frame.name.into()).unwrap(); | ||
let filename = String::from_utf8(miri_frame.filename.into()).unwrap(); | ||
|
||
// Print every frame to stderr. | ||
let out = format!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name); | ||
eprintln!("{}", out); | ||
// Print the 'main' frame (and everything before it) to stdout, skipping | ||
// the printing of internal (and possibly fragile) libstd frames. | ||
if !seen_main { | ||
println!("{}", out); | ||
seen_main = name == "main"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
$DIR/backtrace-api.rs:21:59 (func_c) | ||
$DIR/backtrace-api.rs:20:53 (func_b) | ||
$DIR/backtrace-api.rs:19:50 (func_a) | ||
$DIR/backtrace-api.rs:25:18 (main) | ||
RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())) | ||
RUSTLIB/src/rust/library/std/src/sys_common/backtrace.rs:LL:COL (std::sys_common::backtrace::__rust_begin_short_backtrace) | ||
RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start::{closure#0}) | ||
RUSTLIB/src/rust/library/core/src/ops/function.rs:LL:COL (std::ops::function::impls::call_once) | ||
RUSTLIB/src/rust/library/std/src/panicking.rs:LL:COL (std::panicking::r#try::do_call) | ||
RUSTLIB/src/rust/library/std/src/panicking.rs:LL:COL (std::panicking::r#try) | ||
RUSTLIB/src/rust/library/std/src/panic.rs:LL:COL (std::panic::catch_unwind) | ||
RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start_internal) | ||
RUSTLIB/src/rust/library/std/src/rt.rs:LL:COL (std::rt::lang_start) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
$DIR/backtrace-api.rs:21:59 (func_c) | ||
$DIR/backtrace-api.rs:20:53 (func_b::<u8>) | ||
$DIR/backtrace-api.rs:19:50 (func_a) | ||
$DIR/backtrace-api.rs:25:18 (main) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be
repr(C)
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use
mplace_field
to write to fields, which ends up usingFieldsShape
to map the definition-order fields to their actual offsets in memory.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh so we exploit that we actually have access to the user-defined type... sneaky.^^