Is it possible to print a backtrace in Rust without panicking? - rust

Is it possible to print a backtrace (assuming RUST_BACKTRACE is enabled) without panicking? It seems that the only way of doing that is calling via panic!. If not, is there a reason for it?

Rust uses the backtrace crate to print the backtrace in case of panics (has been merged in PR #60852).
A simple example can be found in the crate documentation
use backtrace::Backtrace;
fn main() {
let bt = Backtrace::new();
// do_some_work();
println!("{:?}", bt);
}
which gives for example
stack backtrace:
0: playground::main::h6849180917e9510b (0x55baf1676201)
at src/main.rs:4
1: std::rt::lang_start::{{closure}}::hb3ceb20351fe39ee (0x55baf1675faf)
at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/rt.rs:64
2: {{closure}} (0x55baf16be492)
at src/libstd/rt.rs:49
do_call<closure,i32>
at src/libstd/panicking.rs:293
3: __rust_maybe_catch_panic (0x55baf16c00b9)
at src/libpanic_unwind/lib.rs:87
4: try<i32,closure> (0x55baf16bef9c)
at src/libstd/panicking.rs:272
catch_unwind<closure,i32>
at src/libstd/panic.rs:388
lang_start_internal
at src/libstd/rt.rs:48
5: std::rt::lang_start::h2c4217f9057b6ddb (0x55baf1675f88)
at /rustc/3c235d5600393dfe6c36eeed34042efad8d4f26e/src/libstd/rt.rs:64
6: main (0x55baf16762f9)
7: __libc_start_main (0x7fab051b9b96)
8: _start (0x55baf1675e59)
9: <unknown> (0x0)

You can use std::backtrace::Backtrace since rust 1.65.0:
use std::backtrace::Backtrace;
fn main() {
println!("Custom backtrace: {}", Backtrace::capture());
// ... or forcibly capture the backtrace regardless of environment variable configuration
println!("Custom backtrace: {}", Backtrace::force_capture());
}
Documentation: https://doc.rust-lang.org/std/backtrace/struct.Backtrace.html

Related

Remove superfluous `andi ..., 0xff` instruction from inline assembly when outputting an `i8`

I have the following function using inline assembly, targeting mipsel-unknown-linux-gnu:
#![feature(asm)]
#[no_mangle]
pub unsafe extern "C" fn f(ptr: u32) {
let value: i8;
asm!(
"lb $v0, ($a0)",
in("$4") ptr,
out("$2") value,
);
asm!(
"sb $v0, ($a0)",
in("$4") ptr,
in("$2") value,
);
}
I expected this to compile into the following:
lb $v0, ($a0)
sb $v0, ($a0)
jr $ra
nop
Note: In this example, it's possible the compiler reorders the store instruction to after the jump to use the delay slot, but in my actual use case, I return via an asm block, so this is not a worry. Given this I expected the assembly above exactly.
Instead, what I got was:
00000000 <f>:
0: 80820000 lb v0,0(a0)
4: 304200ff andi v0,v0,0xff
8: a0820000 sb v0,0(a0)
c: 03e00008 jr ra
10: 00000000 nop
The compiler seems to not have trusted me that the output is an i8 and inserted an andi $v0, 0xff instruction there.
I need to produce the assembly I specified above exactly, so I'd like to get rid of the andi instruction, while keeping the type of value i8.
My use case for this is that I want to produce an exact assembly output from this function, while being able to later fork it and add rust code that interacts with the existing assembly code to extend the function. For this I'd like value's type to be properly described as an i8 in the rust side.
Edit
Looking at the llvm-ir generated by rust, the andi instruction seems to have been added by rustc, not llvm.
; Function Attrs: nonlazybind uwtable
define void #f(i32 %ptr) unnamed_addr #0 {
start:
%0 = tail call i32 asm sideeffect alignstack "lbu $$v0, ($$a0)", "=&{$2},{$4},~{memory}"(i32 %ptr) #1, !srcloc !2
%1 = and i32 %0, 255 # <--------- Over here
tail call void asm sideeffect alignstack "sb $$v0, ($$a0)", "{$4},{$2},~{memory}"(i32 %ptr, i32 %1) #1, !srcloc !3
ret void
}
There is also no mention of an i8, so I'm not quite sure what rustc is doing here.

why does max_by_key with i32.log10() causing "attempt to add with overflow"

I'm trying to get the max number of digits in an i32 array. I'm using log10(n) + 1 as my formula to calculate how many digits an i32 has and I thought I would just be able to use that within a max_by_key, but I am getting
thread 'main' panicked at 'attempt to add with overflow', /mnt/f/Personal-Docs/Repos/radix_sort_rs/src/lib/lib.rs:9:60
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Here's my code:
fn get_max_digits<const N: usize>(arr: [i32; N]) -> i32 {
return *arr.iter().max_by_key(|a| a.log10() + 1).unwrap();
}
I'm still new to rust so sorry if this is a simple question.
Figured out the answer to my question not too long after posting. I was trying to do a logarithm of 0 causing undefined. I believe the attempt to add with overflow is the current interaction with 0i32.log10() as it is a nightly only feature.

Cargo build --release option ignored when testing overflow

I've been stepping through the Programming Rust book and wanted to observe the two's complement wrapping, so simple code of:
fn main() {
let mut x: u8 = 255;
println!("the value of x is {}", x) ;
x = 255 + 1 ;
println!("The value of x now is {}",x) ;
}
when I try and compile this with Cargo as per the guide, I run
cargo build --release
which in the book says will let it compile without overflow protection, but it won't compile. I get the protection error
|
6 | x = 255 + 1 ;
| ^^^^^^^^^^^ attempt to compute u8::MAX + 1_u8, which would overflow
Can you explain what I'm doing wrong please ?
I believe the value is not checked dynamically during run-time (it wont panic and would overflow) but still statically checked for (if possible) during compile time.
In this case the compiler is able to determine at compile time what you're trying to do and prevents you from doing it.
That being said if you look at the compiler output you can see the following message:
note: #[deny(arithmetic_overflow)] on by default
You'll see this message regardless of the optimization level.
If you'd like to observe the overflow put the following inner attribute at the top of your source file.
#![allow(arithmetic_overflow)]
Or, if you're compiling with rustc directly you can pass the following flags:
-O -A arithmetic_overflow
The rustc docs show that the following lints are on by default (regardless of optimization level)
ambiguous_associated_items
arithmetic_overflow
conflicting_repr_hints
const_err
ill_formed_attribute_input
incomplete_include
invalid_type_param_default
macro_expanded_macro_exports_accessed_by_absolute_paths
missing_fragment_specifier
mutable_transmutes
no_mangle_const_items
order_dependent_trait_objects
overflowing_literals
patterns_in_fns_without_body
pub_use_of_private_extern_crate
soft_unstable
unconditional_panic
unknown_crate_types
useless_deprecated
When you write a literal 255+1 in your code, the compiler evaluates the expression at compile-time and sees the overflow immediately, whether in debug or release mode. When the book says that --release disables overflow protection, it's talking about runtime checks. You can see the difference with this code:
fn increment (x: u8) -> u8 { x + 1 }
fn main() {
let x = 255;
println!("x: {}, x+1: {}", x, increment (x));
}
Playground
If you run this code in debug mode, you get:
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:1:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
But if you run it in release mode, you get:
x: 255, x+1: 0

cargo test with no_std fails with error code 176, 160

Using the below code in main.rs, when I run cargo test it returns error code 176, when I add a test or `any statement in main function. It starts returning error code 160.
#![no_std]
#![no_main]
#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(not(test))]
extern crate panic_halt;
#[no_mangle]
pub unsafe extern "C" fn main() {
}
From this link I found out, that
Exit code 176 means "Unable to install on a network drive. Select
another install location in your preferences and retry installing."
When I tried to find back trace through lldb it returned
error: invalid thread
I couldn't find any thread which mentions similar error, so asking it here.
Any help is appreciated.
Using nightly (nightly-x86_64-apple-darwin)
Thanks.
The problem is your main function, which has an invalid signature for unix platforms. It must return an i32 (or better c_int, but for simplicity we suppose they are idential).
The 176 is just some random value in the rax register when leaving the main function.
So change your main signature to:
pub unsafe extern "C" fn main() -> i32
and return a return code (e.g. 0 which means success)
or
pub unsafe extern "C" fn main() -> !
and use the exit syscall on linux (or an infinite loop on embedded systems).

LLVM produced by rustc gives error about argument type of main when run with lli

I'm trying to learn a little about the LLVM IR, particularly what exactly rustc outputs. I'm having a little bit of trouble running even a very simple case.
I put the following in a source file simple.rs:
fn main() {
let x = 7u32;
let y = x + 2;
}
and run rustc --emit llvm-ir simple.rs to get the file simple.ll, containing
; ModuleID = 'simple.cgu-0.rs'
source_filename = "simple.cgu-0.rs"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: uwtable
define internal void #_ZN6simple4main17h8ac50d7470339b75E() unnamed_addr #0 {
start:
br label %bb1
bb1: ; preds = %start
ret void
}
define i64 #main(i64, i8**) unnamed_addr {
top:
%2 = call i64 #_ZN3std2rt10lang_start17ha09816a4e25587eaE(void ()* #_ZN6simple4main17h8ac50d7470339b75E, i64 %0, i8** %1)
ret i64 %2
}
declare i64 #_ZN3std2rt10lang_start17ha09816a4e25587eaE(void ()*, i64, i8**) unnamed_addr
attributes #0 = { uwtable }
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"PIE Level", i32 2}
I then try to run this with the command
lli-3.9 -load ~/.multirust/toolchains/nightly-x86_64-unknown-linux-gnu/lib/libstd-35ad9950c7e5074b.so simple.ll
but I get the error message
LLVM ERROR: Invalid type for first argument of main() supplied
I'm able to make a minimal reproduction of this as follows: I make a file called s2.ll, containing
define i32 #main(i64, i8**) {
ret i32 42
}
and running lli-3.9 s2.ll gives the same error message. But if I change the contents of s2.ll to
define i32 #main(i32, i8**) {
ret i32 42
}
(i.e. I've changed the type of argc in main) then lli-3.9 s2.ll runs, and echo $? reveals that it did indeed return 42.
I don't think I should have to pass in the i64 explicitly - my argument list or C strings should be put into memory somewhere and the pointer and length passed to main automatically, right? Therefore I assume that I'm doing something wrong in the way in invoke lli - but I have no idea what.
Rust marks its entry point (the function marked with #[start] attribute, by default the function lang_start in the standard library) as taking an argc parameter of type isize. This is a bug because it should have the type of a C int, so it should be 32 bits on a 64-bit platform, but isize is 64 bits. However, due to the way 64-bit calling conventions work, this happens to still work correctly. The same issue also exists for the return type.
A fix for this has been committed on 2017-10-01 and should be present in Rust 1.22.
lli is apparently more strict about checking the type of main which is why it gives the error. But if you use llc instead, it should work correctly.
To get the correct main signature, you can cancel the default main by putting #![no_main] at the top of the module, and provide your own main marked with #[no_mangle]. But note that this will skip the standard library's initialization.
#![no_main]
#[no_mangle]
pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 {
0
}
See also:
documentation about lang_items and disabling main
Tracking issue for #[start] feature, where some people mention isize not being correct.

Resources