riscvarchive / riscv-binutils-gdb

RISC-V backports for binutils-gdb. Development is done upstream at the FSF.
GNU General Public License v2.0
148 stars 233 forks source link

GDB RISC-V Hardware Breakpoint Support #228

Closed Hzv587 closed 3 years ago

Hzv587 commented 3 years ago

Hi Guys,

I'm currently working on my own on-chip debug system. To verify the functionality, I run GDB, openOCD on PC to communicate with FPGA. Everything works reasonably, except when I try to use 'hb line number' in GDB. image

As you can see, the gdb reports that it can not access the memory. By further digging into openOCD debug log message, image GDB always try to first get the instruction value in the instruction memory. But since I only want to let the system stop at the certain instruction address, why GDB bother accessing instruction memory. Is this the expected behaviour?

I'm looking forward to your feedback, thanks a lot!

jim-wilson commented 3 years ago

The RISC-V gdb port doesn't support hardware breakpoints. I think openocd does. Gdb has to write an ebreak instruction at the target address, and to do that, we need to know whether it is a 16-bit compressed instruction or a regular 32-bit instruction, and the safest way to do that is to read the low byte of the instruction. So yes, gdb needs to read instruction memory by default.

There is a setting "show riscv use-compressed-breakpoints" which is auto by default. If you set that to on or off then we won't read instruction memory, we will just always use a 2 or 4 byte ebreak. You still need to write to instruction memory though, and if you can't read memory you probably can't write memory either.

There is no dedicated RISC-V gdb developer, and the design of the RISC-V debug support makes things complicated for gdb, since debug registers can only be set over jtag, and gdb doesn't talk jtag, and may not know if it is connected via jtag. Openocd does talk jtag, so openocd can set hardware breakpoints, but gdb can't currently.

timsifive commented 3 years ago

RISC-V native gdb (gdb debugging a Linux process on a RISC-V system) probably does not support hardware breakpoints. When a debugger like OpenOCD is used, gdb does support hardware breakpoints. In this case gdb sends a Z1 packet to OpenOCD, which sets the breakpoint. I don't know why gdb insists on reading from that memory address first, but it's possible gdb is trying to disassemble the instruction to set the size to the "correct" length. It is, however, perfectly legitimate to set a trigger with size 0, in which case any sized access at that address will cause the trigger to match. (I'm not even sure that gdb can pass the size on to OpenOCD.)

Contrary to what Jim said, hardware breakpoints (called triggers) can be set simply by writing CSRs, with the explicit intention that native gdb/gdbserver can set them. So if you need a work-around for this gdb problem, you can try writing the CSRs directly. OpenOCD is pretty careful when it comes to managing triggers, and will not overwrite a trigger that's been set manually by a user.

The bottom line is that nobody is really working on this right now. :-(

Hzv587 commented 3 years ago

The RISC-V gdb port doesn't support hardware breakpoints. I think openocd does. Gdb has to write an ebreak instruction at the target address, and to do that, we need to know whether it is a 16-bit compressed instruction or a regular 32-bit instruction, and the safest way to do that is to read the low byte of the instruction. So yes, gdb needs to read instruction memory by default.

There is a setting "show riscv use-compressed-breakpoints" which is auto by default. If you set that to on or off then we won't read instruction memory, we will just always use a 2 or 4 byte ebreak. You still need to write to instruction memory though, and if you can't read memory you probably can't write memory either.

There is no dedicated RISC-V gdb developer, and the design of the RISC-V debug support makes things complicated for gdb, since debug registers can only be set over jtag, and gdb doesn't talk jtag, and may not know if it is connected via jtag. Openocd does talk jtag, so openocd can set hardware breakpoints, but gdb can't currently.

Hi Jim, thanks for the reply! After I enabled instruction memory read in my RISC-V system, I do see GDB tries to use CSRs defined as trigger modules in the debug spec. So GDB + openOCD supports hardware breakpoints. But thanks for the inputs on software breakpoints, it makes sense to write instruction memory to insert EBREAK instructions.

Hzv587 commented 3 years ago

RISC-V native gdb (gdb debugging a Linux process on a RISC-V system) probably does not support hardware breakpoints. When a debugger like OpenOCD is used, gdb does support hardware breakpoints. In this case gdb sends a Z1 packet to OpenOCD, which sets the breakpoint. I don't know why gdb insists on reading from that memory address first, but it's possible gdb is trying to disassemble the instruction to set the size to the "correct" length. It is, however, perfectly legitimate to set a trigger with size 0, in which case any sized access at that address will cause the trigger to match. (I'm not even sure that gdb can pass the size on to OpenOCD.)

Contrary to what Jim said, hardware breakpoints (called triggers) can be set simply by writing CSRs, with the explicit intention that native gdb/gdbserver can set them. So if you need a work-around for this gdb problem, you can try writing the CSRs directly. OpenOCD is pretty careful when it comes to managing triggers, and will not overwrite a trigger that's been set manually by a user.

The bottom line is that nobody is really working on this right now. :-(

Hi Tim, thanks for the inputs, I'm currently digging a little bit further in that. But I get the feeling that GDB only supports address triggers, no data triggers, am I right?

timsifive commented 3 years ago

There is a syntax for data triggers in gdb using conditional breakpoints, but what happens in practice is you end up with an address trigger that fires every time, and then gdb performs the data comparison before deciding to silently resume or show the user the target halted. There may be a way to do it properly, but I haven't really investigated.

Hzv587 commented 3 years ago

There is a syntax for data triggers in gdb using conditional breakpoints, but what happens in practice is you end up with an address trigger that fires every time, and then gdb performs the data comparison before deciding to silently resume or show the user the target halted. There may be a way to do it properly, but I haven't really investigated.

that's very good inputs, thanks.