Skip to content

Ignore panic functions in backtraces. Print inlined functions on Windows #45637

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions src/libcore/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,38 @@ pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! {
// Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
// output binary, saving up to a few kilobytes.
let (expr, file, line, col) = *expr_file_line_col;
panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col))
panic_dispatch(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col), &(panic as usize))
}

#[cold] #[inline(never)]
#[lang = "panic_bounds_check"]
fn panic_bounds_check(file_line_col: &(&'static str, u32, u32),
index: usize, len: usize) -> ! {
panic_fmt(format_args!("index out of bounds: the len is {} but the index is {}",
len, index), file_line_col)
index: usize, len: usize) -> ! {
panic_dispatch(format_args!("index out of bounds: the len is {} but the index is {}",
len, index),
file_line_col,
&(panic_bounds_check as usize))
}

#[cold] #[inline(never)]
pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
panic_dispatch(fmt, file_line_col, &(panic_fmt as usize));
}

#[cold]
fn panic_dispatch(fmt: fmt::Arguments,
file_line_col: &(&'static str, u32, u32),
entry_point: &usize) -> ! {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this is passed as &usize? Can't this just be a usize?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think passing locals by reference also prevents tail call optimization. I'm not really sure that this or the asm! trick works for function which never return. Currently these do not get tail call optimization applied, but I don't see a reason for that. Perhaps LLVM skips that optimization on cold functions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little wary to have this and rely on this, are we sure that this doesn't get optimized away even with LTO? If we leave this in can it at least be documented in the source as to why it's a reference?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess for why this isn't optimized is because fmt::Arguments is a pretty big struct that's copied around the stack, and LLVM is probably conservative with the tail-call for now. Despite that though this seems like something that's pretty brittle to rely on?

#[allow(improper_ctypes)]
extern {
#[lang = "panic_fmt"]
#[unwind]
fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !;
fn panic_impl(fmt: fmt::Arguments,
file: &'static str,
line: u32,
col: u32,
entry_point: &usize) -> !;
}
let (file, line, col) = *file_line_col;
unsafe { panic_impl(fmt, file, line, col) }
}
unsafe { panic_impl(fmt, file, line, col, entry_point) }
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline

26 changes: 18 additions & 8 deletions src/libstd/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
pub struct PanicInfo<'a> {
payload: &'a (Any + Send),
location: Location<'a>,
entry_point: usize,
}

impl<'a> PanicInfo<'a> {
Expand Down Expand Up @@ -378,7 +379,7 @@ fn default_hook(info: &PanicInfo) {
static FIRST_PANIC: AtomicBool = AtomicBool::new(true);

if let Some(format) = log_backtrace {
let _ = backtrace::print(err, format);
let _ = backtrace::print(err, format, info.entry_point);
} else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) {
let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace.");
}
Expand Down Expand Up @@ -494,8 +495,9 @@ pub fn panicking() -> bool {
pub extern fn rust_begin_panic(msg: fmt::Arguments,
file: &'static str,
line: u32,
col: u32) -> ! {
begin_panic_fmt(&msg, &(file, line, col))
col: u32,
entry_point: &usize) -> ! {
rust_panic_fmt(&msg, &(file, line, col), entry_point)
}

/// The entry point for panicking with a formatted message.
Expand All @@ -508,8 +510,14 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
reason = "used by the panic! macro",
issue = "0")]
#[inline(never)] #[cold]
pub fn begin_panic_fmt(msg: &fmt::Arguments,
file_line_col: &(&'static str, u32, u32)) -> ! {
pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
rust_panic_fmt(msg, file_line_col, &(begin_panic_fmt as usize))
}

#[cold]
fn rust_panic_fmt(msg: &fmt::Arguments,
file_line_col: &(&'static str, u32, u32),
entry_point: &usize) -> ! {
use fmt::Write;

// We do two allocations here, unfortunately. But (a) they're
Expand All @@ -519,7 +527,7 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,

let mut s = String::new();
let _ = s.write_fmt(*msg);
begin_panic(s, file_line_col)
rust_panic_with_hook(Box::new(s), file_line_col, entry_point)
}

/// This is the entry point of panicking for panic!() and assert!().
Expand All @@ -535,7 +543,7 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
// be performed in the parent of this thread instead of the thread that's
// panicking.

rust_panic_with_hook(Box::new(msg), file_line_col)
rust_panic_with_hook(Box::new(msg), file_line_col, &(begin_panic::<M> as usize))
}

/// Executes the primary logic for a panic, including checking for recursive
Expand All @@ -547,7 +555,8 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
#[inline(never)]
#[cold]
fn rust_panic_with_hook(msg: Box<Any + Send>,
file_line_col: &(&'static str, u32, u32)) -> ! {
file_line_col: &(&'static str, u32, u32),
entry_point: &usize) -> ! {
let (file, line, col) = *file_line_col;

let panics = update_panic_count(1);
Expand All @@ -571,6 +580,7 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
line,
col,
},
entry_point: *entry_point,
};
HOOK_LOCK.read();
match HOOK {
Expand Down
11 changes: 10 additions & 1 deletion src/libstd/rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
// Reexport some of our utilities which are expected by other crates.
pub use panicking::{begin_panic, begin_panic_fmt, update_panic_count};

#[cfg(feature = "backtrace")]
pub use sys_common::backtrace::mark_start as mark_backtrace_start;

#[cfg(not(feature = "backtrace"))]
pub fn mark_backtrace_start(f: &mut FnMut()) {
f();
}

#[cfg(not(test))]
#[lang = "start"]
fn lang_start(main: fn(), argc: isize, argv: *const *const u8) -> isize {
Expand Down Expand Up @@ -56,7 +64,8 @@ fn lang_start(main: fn(), argc: isize, argv: *const *const u8) -> isize {
// Let's run some code!
#[cfg(feature = "backtrace")]
let res = panic::catch_unwind(|| {
::sys_common::backtrace::__rust_begin_short_backtrace(main)
let mut main = main;
::sys_common::backtrace::mark_start(&mut main)
});
#[cfg(not(feature = "backtrace"))]
let res = panic::catch_unwind(mem::transmute::<_, fn()>(main));
Expand Down
8 changes: 4 additions & 4 deletions src/libstd/sys/unix/backtrace/printing/dladdr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ use sys_common::backtrace::Frame;
pub fn resolve_symname<F>(frame: Frame,
callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
where F: FnOnce(Option<(&str, usize)>) -> io::Result<()>
{
unsafe {
let mut info: Dl_info = intrinsics::init();
let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
let syminfo = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
info.dli_sname.is_null() {
None
} else {
CStr::from_ptr(info.dli_sname).to_str().ok()
CStr::from_ptr(info.dli_sname).to_str().ok().map(|s| (s, info.dli_saddr as usize))
};
callback(symname)
callback(syminfo)
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/libstd/sys/unix/backtrace/printing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ pub use sys_common::gnu::libbacktrace::foreach_symbol_fileline;
#[cfg(not(target_os = "emscripten"))]
pub fn resolve_symname<F>(frame: Frame, callback: F, bc: &BacktraceContext) -> io::Result<()>
where
F: FnOnce(Option<&str>) -> io::Result<()>
F: FnOnce(Option<(&str, usize)>) -> io::Result<()>
{
::sys_common::gnu::libbacktrace::resolve_symname(frame, |symname| {
if symname.is_some() {
callback(symname)
::sys_common::gnu::libbacktrace::resolve_symname(frame, |syminfo| {
if syminfo.is_some() {
callback(syminfo)
} else {
dladdr::resolve_symname(frame, callback, bc)
}
Expand Down
1 change: 1 addition & 0 deletions src/libstd/sys/unix/backtrace/tracing/gcc_s.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
cx.frames[cx.idx] = Frame {
symbol_addr: symaddr as *mut u8,
exact_position: ip as *mut u8,
inline_context: 0,
};
cx.idx += 1;
}
Expand Down
31 changes: 15 additions & 16 deletions src/libstd/sys/windows/backtrace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ pub fn unwind_backtrace(frames: &mut [Frame])
// Fetch the symbols necessary from dbghelp.dll
let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn)?;
let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn)?;
let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn)?;
let StackWalkEx = sym!(dbghelp, "StackWalkEx", StackWalkExFn)?;

// Allocate necessary structures for doing the stack walk
let process = unsafe { c::GetCurrentProcess() };
let thread = unsafe { c::GetCurrentThread() };
let mut context: c::CONTEXT = unsafe { mem::zeroed() };
unsafe { c::RtlCaptureContext(&mut context) };
let mut frame: c::STACKFRAME64 = unsafe { mem::zeroed() };
let mut frame: c::STACKFRAME_EX = unsafe { mem::zeroed() };
frame.StackFrameSize = mem::size_of_val(&frame) as c::DWORD;
let image = init_frame(&mut frame, &context);

let backtrace_context = BacktraceContext {
Expand All @@ -79,24 +80,22 @@ pub fn unwind_backtrace(frames: &mut [Frame])
}

// And now that we're done with all the setup, do the stack walking!
// Start from -1 to avoid printing this stack frame, which will
// always be exactly the same.
let mut i = 0;
unsafe {
while i < frames.len() &&
StackWalk64(image, process, thread, &mut frame, &mut context,
StackWalkEx(image, process, thread, &mut frame, &mut context,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut()) == c::TRUE
ptr::null_mut(),
0) == c::TRUE
{
let addr = frame.AddrPC.Offset;
if addr == frame.AddrReturn.Offset || addr == 0 ||
frame.AddrReturn.Offset == 0 { break }
let addr = (frame.AddrPC.Offset - 1) as *const u8;

frames[i] = Frame {
symbol_addr: (addr - 1) as *const u8,
exact_position: (addr - 1) as *const u8,
symbol_addr: addr,
exact_position: addr,
inline_context: frame.InlineFrameContext,
};
i += 1;
}
Expand All @@ -111,14 +110,14 @@ type SymInitializeFn =
type SymCleanupFn =
unsafe extern "system" fn(c::HANDLE) -> c::BOOL;

type StackWalk64Fn =
type StackWalkExFn =
unsafe extern "system" fn(c::DWORD, c::HANDLE, c::HANDLE,
*mut c::STACKFRAME64, *mut c::CONTEXT,
*mut c::STACKFRAME_EX, *mut c::CONTEXT,
*mut c_void, *mut c_void,
*mut c_void, *mut c_void) -> c::BOOL;
*mut c_void, *mut c_void, c::DWORD) -> c::BOOL;

#[cfg(target_arch = "x86")]
fn init_frame(frame: &mut c::STACKFRAME64,
fn init_frame(frame: &mut c::STACKFRAME_EX,
ctx: &c::CONTEXT) -> c::DWORD {
frame.AddrPC.Offset = ctx.Eip as u64;
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
Expand All @@ -130,7 +129,7 @@ fn init_frame(frame: &mut c::STACKFRAME64,
}

#[cfg(target_arch = "x86_64")]
fn init_frame(frame: &mut c::STACKFRAME64,
fn init_frame(frame: &mut c::STACKFRAME_EX,
ctx: &c::CONTEXT) -> c::DWORD {
frame.AddrPC.Offset = ctx.Rip as u64;
frame.AddrPC.Mode = c::ADDRESS_MODE::AddrModeFlat;
Expand Down
63 changes: 40 additions & 23 deletions src/libstd/sys/windows/backtrace/printing/msvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,27 @@ use ffi::CStr;
use io;
use libc::{c_ulong, c_char};
use mem;
use ptr;
use sys::c;
use sys::backtrace::BacktraceContext;
use sys_common::backtrace::Frame;

type SymFromAddrFn =
unsafe extern "system" fn(c::HANDLE, u64, *mut u64,
*mut c::SYMBOL_INFO) -> c::BOOL;
type SymGetLineFromAddr64Fn =
unsafe extern "system" fn(c::HANDLE, u64, *mut u32,
*mut c::IMAGEHLP_LINE64) -> c::BOOL;
type SymFromInlineContextFn =
unsafe extern "system" fn(c::HANDLE, u64, c::DWORD,
*mut u64, *mut c::SYMBOL_INFO) -> c::BOOL;
type SymGetLineFromInlineContextFn =
unsafe extern "system" fn(c::HANDLE, u64, c::DWORD, c::HMODULE,
*mut u32, *mut c::IMAGEHLP_LINE64) -> c::BOOL;

/// Converts a pointer to symbol to its string value.
pub fn resolve_symname<F>(frame: Frame,
callback: F,
context: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
where F: FnOnce(Option<(&str, usize)>) -> io::Result<()>
{
let SymFromAddr = sym!(&context.dbghelp, "SymFromAddr", SymFromAddrFn)?;
let SymFromInlineContext = sym!(&context.dbghelp,
"SymFromInlineContext",
SymFromInlineContextFn)?;

unsafe {
let mut info: c::SYMBOL_INFO = mem::zeroed();
Expand All @@ -40,18 +43,30 @@ pub fn resolve_symname<F>(frame: Frame,
info.SizeOfStruct = 88;

let mut displacement = 0u64;
let ret = SymFromAddr(context.handle,
frame.symbol_addr as u64,
&mut displacement,
&mut info);

let symname = if ret == c::TRUE {
let ret = SymFromInlineContext(context.handle,
frame.symbol_addr as u64,
frame.inline_context,
&mut displacement,
&mut info);
let valid_range = if ret == c::TRUE &&
frame.symbol_addr as usize >= info.Address as usize {
if info.Size != 0 {
(frame.symbol_addr as usize) < info.Address as usize + info.Size as usize
} else {
true
}
} else {
false
};
let syminfo = if valid_range {
let ptr = info.Name.as_ptr() as *const c_char;
CStr::from_ptr(ptr).to_str().ok()
CStr::from_ptr(ptr).to_str().ok().map(|s| {
(s, info.Address as usize)
})
} else {
None
};
callback(symname)
callback(syminfo)
}
}

Expand All @@ -61,19 +76,21 @@ pub fn foreach_symbol_fileline<F>(frame: Frame,
-> io::Result<bool>
where F: FnMut(&[u8], u32) -> io::Result<()>
{
let SymGetLineFromAddr64 = sym!(&context.dbghelp,
"SymGetLineFromAddr64",
SymGetLineFromAddr64Fn)?;
let SymGetLineFromInlineContext = sym!(&context.dbghelp,
"SymGetLineFromInlineContext",
SymGetLineFromInlineContextFn)?;

unsafe {
let mut line: c::IMAGEHLP_LINE64 = mem::zeroed();
line.SizeOfStruct = ::mem::size_of::<c::IMAGEHLP_LINE64>() as u32;

let mut displacement = 0u32;
let ret = SymGetLineFromAddr64(context.handle,
frame.exact_position as u64,
&mut displacement,
&mut line);
let ret = SymGetLineFromInlineContext(context.handle,
frame.exact_position as u64,
frame.inline_context,
ptr::null_mut(),
&mut displacement,
&mut line);
if ret == c::TRUE {
let name = CStr::from_ptr(line.Filename).to_bytes();
f(name, line.LineNumber as u32)?;
Expand Down
4 changes: 3 additions & 1 deletion src/libstd/sys/windows/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ pub struct ADDRESS64 {

#[repr(C)]
#[cfg(feature = "backtrace")]
pub struct STACKFRAME64 {
pub struct STACKFRAME_EX {
pub AddrPC: ADDRESS64,
pub AddrReturn: ADDRESS64,
pub AddrFrame: ADDRESS64,
Expand All @@ -636,6 +636,8 @@ pub struct STACKFRAME64 {
pub Virtual: BOOL,
pub Reserved: [u64; 3],
pub KdHelp: KDHELP64,
pub StackFrameSize: DWORD,
pub InlineFrameContext: DWORD,
}

#[repr(C)]
Expand Down
Loading