Rusts current pretty printers in lldb and gdb are just not good enough for a fluid step debugging experience. I've had luck with intellij IDE's but it's very sad that the best we can do in a language with such good devex tooling is print debugging. Theoretically you could generate debug scripts with a macro and embed them with #![debugger_visualizer(gdb_script_file = "../foo.py")] but I haven't seen anyone go through…
Show HN: Unbug – Rust macros for programmatically invoking breakpoints
31–40 of 41 posts
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#32What does nightly mean ? I hate that you could not know a specific version of a nightly.
Per https://dev-doc.rust-lang.org/nightly/unstable-book/library-...
>This feature is internal to the Rust compiler and is not intended for general use.
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#33Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#34Earlier quoted context omitted.
Plain int3 is a footgun: the CPU does not keep track of the address of the int3 (at least not until FRED), and it reports the address after int3. It’s impossible to reliably undo that in software, and most debuggers don’t even try, and the result is a failure to identify the location of the breakpoint. It’s problematic if the int3 is the last instruction in a basic block, and even worse if the optimizer thinks that w…
The "canonical" INT 3 is a single byte opcode (CCh), so the debugger can just subtract 1 from the address pushed on the stack to get the breakpoint location. There is another encoding (CD 03), but no assembler should emit it. It used to be possible for adversarial code to confuse debug interrupt handlers with this, but this should be fixed now.
int3 is a "trap". continue will resume execution at the instruction after int3, as intended. But backtrace should, by some ill-defined magic, generate the backtrace as though RIP was (saved RIP - 1). And the condition for doing this isn't something that is (AFAIK) representable at all in GCC's worldview. Sure, GCC knows, or at least ought to know [0], that it gained control because of vector 3, and the Intel and AMD manuals say that vector 3 is a trap. But there isn't a bit in memory or anything you would see in 'info regs' that will say "hey, this is a 'trap', and backtraces and such should be done as though RIP was actually RIP-1".
Maybe the right solution would be to split the program counter, from the perspective of the debugger, into two fields: program counter for backtracing, and program counter for resumption.
And yes, I know that GCC gets this wrong. Been there, seen the failures. I have not checked, but I expect that LLDB works exactly like GCC in this regard.
[0] ptrace on Linux exposes the vector number, somewhat awkwardly. Or you can infer it from the fact that the signal was SIGTRAP.
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#35Neat project! Maybe this decision is copied over from unreal engine, but instead of `ensure` and `ensure_always`, having names like `ensure_once` and `ensure` would have been more clear to me.
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#36Is there a good newby tutorial on how to use debugger with Rust (and debugger in general?) No videos please.
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#37Earlier quoted context omitted.
Hah, the README says: > Additonally, debugging may not land on the macro statements themselves. See my comment above, and give int3;nop a try.
Interesting. Unfortunately, I'm not well versed in assembly, is there a similar trick or requirement in arm and would that include Apple silicon, or is this something specific to `int3` on x86? That may explain why it was inconsistent during my development process, I didn't think to check if the inconsistency was platform dependent.
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#38Neat project! Maybe this decision is copied over from unreal engine, but instead of `ensure` and `ensure_always`, having names like `ensure_once` and `ensure` would have been more clear to me.
I can understand where you're coming from, but when programming games you generally don't want a breakpoint to be hit more than once since you are running a loop over multiple frames. So in this case the concept of ensure_once is more common, so the shorter inverse is more convenient. Asserts should be enough to get your attention and not to annoy, so orienting it this way is a deliberate choice.
Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#39You could potentially build on stable Rust by emitting the breakpoint instructions yourself, at least on popular platforms. For instance, `core::arch::asm!("int3")` on x86, or `core::arch::asm!("brk #1")` on ARM. Also, this is providing motivation to want to stabilize a breakpoint mechanism, perhaps `core::arch::breakpoint()`. I'm going to propose an API Change Proposal (ACP) to the libs-api team to see if we can pro…
Thanks, yeah I considered using the instructions directly, but I was hoping for a more cross-platform option. For my purposes, developing in the Bevy engine, nightly isn't a huge blocker. Yeah, it would be really great to just have breakpoint support in stable Rust, thanks for doing the proposal! I'll consider stable support in the meantime.
This is the macro I use for example:
#[doc(hidden)]
pub use libc as __libc;
// This is a macro instead of a function to ensure the debugger shows the breakpoint as being at
// the caller instead of this file.
#[cfg(unix)]
#[macro_export]
macro_rules! breakpoint {
() => {
unsafe {
use $crate::__libc as libc;
libc::raise(libc::SIGTRAP);
}
};
}Re: Show HN: Unbug – Rust macros for programmatically invoking breakpoints
#40Earlier quoted context omitted.
The "canonical" INT 3 is a single byte opcode (CCh), so the debugger can just subtract 1 from the address pushed on the stack to get the breakpoint location. There is another encoding (CD 03), but no assembler should emit it. It used to be possible for adversarial code to confuse debug interrupt handlers with this, but this should be fixed now.
This would involve the debugger actually being structured in a way that makes this make sense. A debugger like GCC has a gnarly data structure that represents the machine state, and it contains things like EIP/RIP. There is a command 'backtrace' that takes the machine state and attempts to generate a backtrace. And there's a command 'continue' that resumes execution. int3 is a "trap". continue will resume execution a…
Seems like a deficiency in GDB (and maybe LLDB too), not in the kernel or x86.