It was interesting to see the matter of skipping permanent breakpoints come up on this list. This is very similar to an issue we dealt with a couple of years ago with Xtensa and remote targets. Our solution has been stable for at least two years, but has not yet been prepared for submission because I've had higher priorities (and at that time we were way out of sync with mainline GDB). The issue is actually more complicated than the forgoing discussion suggests. I'd like to discuss it here and present our solution. I'll attach a patch for our current solution relative to GDB 6.8, but I do propose to revise it to improve some areas based on our experience (in the next few weeks) - I'll discuss that toward the end of this post. The Problem ----------- When the target program hits a breakpoint of any kind, GDB receives a SIGTRAP event. As Aleksander pointed out, if the PC is not in the breakpoint table GDB stops because it doesn't know what to do with this event. In fact it doesn't always stop. When GDB is stepping (for example when a software watchpoint is set) it sees the SIGTRAP as a normal step event, so it continues stepping. If the break instruction didn't increment the PC, it keeps executing the same break instruction forever. This is very frustrating for users (who reported this as a bug) because when you have a s/w watchpoint and are stepping, execution is expected to be slow, so you wait a long time before you realize it's hung. Terminology ----------- I use the term "program breakpoint" to denote a break instruction that is part of the program, that GDB did not plant nor know about. There is a feature called "permanent breakpoint" that is a little different in that GDB knows about it (it's in the breakpoint table). I didn't want to use the same term for something different (this is open to discussion). As far as I can tell, only the HPPA arch uses permanent breakpoints. It would be good to reconcile the two, but I haven't attempted that. The Solution ------------ The first part is to recognize when you hit a program break. The second is to step over it when you want to resume. Note that the instruction that represents a program break is entirely arch-specific, and need not be the same one GDB plants for breakpoints. There might be several instructions or variations. One solution we rejected was to have GDB probe the code at the PC (if not at a known planted breakpoint). There are two problems: 1. GDB has to know which instructions to look for. 2. GDB has to probe memory every instruction when stepping, which can seriously hurt performance when there are s/w watchpoints (especially with remote targets). We decided it was best to have the target agent communicate that it is signalling a breakpoint. Note that the target agent might have no idea whether the break was planted by GDB, so it's up to GDB to distinguish by looking up the PC in its breakpoint table. Currently only a SIGTRAP is communicated, and GDB then looks up the PC in its breakpoint table to see if it hit a planted breakpoint. Otherwise, if stepping then GDB assumes it's a single-step trap, else GDB gives up and stops without explanation. For Xtensa we use only the remote protocol. The target signals a "T" stop-reply packet with a trap signal number. It does this whether it hit a break or was stepping. I needed to distinguish the cases. There are already extensions to the "T" packet (eg. "watch", "awatch", "rwatch", "thread"). I added another: TAApbreak:kind where the suffix "kind" is a number that gives arch-specific info about the type of breakpoint, including its size (for skipping). A target interface function and macro is defined so the higher levels of GDB can find out whether it was stopped by a program breakpoint (as opposed to a normal GDB planted breakpoint). STOPPED_BY_PROGRAM_BREAKPOINT(k) returns true or false, and writes the "kind" argument into (int *) k. Some questions arise which I'm sure are hitting your neurons about now. I'll address some of them before I go on. - What about target agents that don't (yet) implement the extension? Behavior is exactly as before: GDB will just treat the SIGTRAP as either a step or an unknown event. - Why "pbreak"? Any extension that starts with letters a-f requires a special case in the parser in remote.c which thinks it's a hex number. There is already a special case to handle "awatch". I wanted to avoid that (it turns out the parser also treats "p" special for currthread, so I will propose using "TAAtrap" - later). - What about non-remote targets? The target agent would have to communicate the information another way. If not, GDB behavior is exactly as before. I'm not sure whether this problem even exists for non-remote targets since I haven't looked into that. We define a target interface function and macro STOPPED_BY_PROGRAM_BREAKPOINT(k) which currently returns 0 (false) for non-remote targets. - Why not use a special signal instead of a remote protocol extension? There is no clear signal that is safe to use. GDB uses just SIGTRAP and SIGINT, and already overloads SIGTRAP for most things. The use of a remote protocol extension to disambiguate is well precedented. - Isn't the size of a breakpoint constant for a given arch? No. There can be several kinds used for different reasons. There can be several encodings on a single break instruction (Xtensa has the normal 3 byte and a "density" 2 byte encoding). - Can't GDB figure out the size of the break by itself? Of course GDB could disassemble the instruction. But that needs another remote protocol exchange to get the code. Why not have the debug agent just pass it along with the signal? Having discovered that it stopped for a program breakpoint, GDB is able to report the fact to both the CLI and MI. I also defined a gdbarch function to report the meaning of "kind" in an arch- specific way. This improves the debugging experience when the target uses different kinds of breakpoints to denote different reasons for stopping. (However, in the interest of simplicity, my revised proposal won't report all that - it is anyway obvious from a disassembly if not a symbol associated with the PC; GDB itself only needs to know the size to skip the breakpoint). When the user elects to continue after a program breakpoint, GDB increments the PC by the size of the break that was reported in the "kind" argument. A gdbarch function handles this. Of course it might do nothing if the PC was already advanced. I haven't discussed all the implementation details here. Please see the attached 6.8 based patch for the actual code. Note this patch does not pretend to be ready for submission! Problems with Current Implementation and Proposed Simplification ---------------------------------------------------------------- 1. "TAApbreak" requires a special case in the remote stop-reply packet parser to differentiate it from {S,T}AA:p. This is really a parser issue, but I'd rather avoid the need to rewrite it or add another special case. I propose "TAAtrap". 2. The "kind" argument of "TAApbreak:kind" can encode some more information the user might be interested in, but the cost is adding two gdbarch functions. Yet the user can figure out the meaning from a disassembly. The only information GDB itself really needs is the size, so it can skip the breakpoint. 3. Non-remote targets aren't handled. However that should be just a matter of adding an implementation to the target interface, and nothing breaks meanwhile. 4. There are cases in which a target OS or debug agent uses a break to trigger an exception of some kind, and then unwinds the stack to before the exception (eg. in the Xtensa ISS). Then when GDB tries to skip the break, it skips real code. Proposed Improved, Simplified Solution -------------------------------------- The remote protocol extension would be: TAAtrap[:size] where ":size" is optional and may only be provided to GDB by the target agent if the PC is in fact pointing to the instruction that caused the break, and if omitted is taken to be 0. GDB will skip the break by PC += size (no effect if size is 0). Note it is not necessary to call gdbarch_breakpoint_from_pc(). The gdbarch functions to extract the size and decipher "kind" are not needed. The target interface function STOPPED_BY_PROGRAM_BREAKPOINT(k) becomes STOPPED_BY_PROGRAM_BREAKPOINT(size) where 'size' is an address in which the size is returned. We might also want to consider calling it "program trap" and keep the term "breakpoint" for things that GDB knows about. Comments are welcome. We at Tensilica would like to see this refined and incorporated into mainline GDB. This can certainly coexist with permanent breakpoints, however it is (I think) a bit more general. If the people who use permanent breakpoints would care to comment, perhaps we can somehow reconcile these into one feature. Thanks, Ross Morley Tensilca, Inc. ross@tensilica.com Aleksandar Ristovski wrote: > Hello, > > When there is a hard-coded breakpoint in code, like in this example > (for x86): > > #include > > int main() > { > __asm(" int $0x03\n"); > printf("Hello World\n"); > return 0; > } > > gdb on linux will appear to work correctly. > > However, on systems that do not need pc adjustment after break (like > QNX) gdb will not be able to step over that breakpoint unless user > explicitly sets a breakpoint on top of it. > > I think that in case of linux it is actually working by accident - > because kernel does not back-up instruction pointer after hard-coded > breakpoint instruction was executed. Gdb will receive SIGTRAP but will > not really know why. > > Attached patch fixes this for systems where > gdbarch_decr_pc_after_break (gdbarch) == 0 > > I am still not sure this is the final fix. Wouldn't it be better if we > recognized a hard-coded breakpoint as a breakpoint? There would be an > issue since it is not in the breakpoint list, but maybe we should > either automatically add it when we encounter it, or perhaps print > with some "special" number (to make it clear to the user it is not one > of the user-generated breakpoints). > > > Thanks, > > Aleksandar Ristovski > QNX Software Systems > > > > ChangeLog for your reference: > > * infrun.c (adjust_pc_after_break): On systems with adjusted PC after > break, make sure hard-coded breakpoint is stepped-over. > >------------------------------------------------------------------------ > >Index: gdb/infrun.c >=================================================================== >RCS file: /cvs/src/src/gdb/infrun.c,v >retrieving revision 1.361 >diff -u -p -r1.361 infrun.c >--- gdb/infrun.c 1 Mar 2009 23:18:36 -0000 1.361 >+++ gdb/infrun.c 16 Mar 2009 17:33:22 -0000 >@@ -2089,14 +2089,29 @@ adjust_pc_after_break (struct execution_ > we have nothing to do. */ > regcache = get_thread_regcache (ecs->ptid); > gdbarch = get_regcache_arch (regcache); >- if (gdbarch_decr_pc_after_break (gdbarch) == 0) >- return; > > /* Find the location where (if we've hit a breakpoint) the > breakpoint would be. */ > breakpoint_pc = regcache_read_pc (regcache) > - gdbarch_decr_pc_after_break (gdbarch); > >+ if (gdbarch_decr_pc_after_break (gdbarch) == 0) >+ { >+ /* Check if we have stopped at a permanent breakpoint. */ >+ int len; >+ const gdb_byte *brk; >+ gdb_byte *target_mem = alloca(32); >+ >+ brk = gdbarch_breakpoint_from_pc (gdbarch, &breakpoint_pc, &len); >+ if (!target_read_memory (breakpoint_pc, target_mem, len) >+ && memcmp (target_mem, brk, len) == 0) >+ { >+ breakpoint_pc += len; >+ regcache_write_pc (regcache, breakpoint_pc); >+ } >+ return; >+ } >+ > /* Check whether there actually is a software breakpoint inserted at > that location. > > >