Opened 6 years ago

Last modified 6 years ago

#9773 assigned enhancement

Add exception breakpoint support

Reported by: bonefish Owned by: anevilyak
Priority: normal Milestone: Unscheduled
Component: Applications/Debugger Version: R1/Development
Keywords: Cc:
Blocked By: #9713 Blocking:
Has a Patch: no Platform: All

Description

Debugger should support exception breakpoints. There are two different kinds:

  • Execution should stop when an exception is thrown.
  • Execution should stop when an exception is caught.

In either case it should be possible to specify for which exception types the execution should stop:

  • any exception type or
  • a set of exception types.

Throw exception breakpoints for any type have the highest implementation priority and should be relatively easy to implement. They mainly require to find out what the ABI specific exception throw function is and to set a breakpoint there.

Change History (12)

comment:1 Changed 6 years ago by anevilyak

Throw exception breakpoints for any type have the highest implementation priority and should be relatively easy to implement. They mainly require to find out what the ABI specific exception throw function is and to set a breakpoint there.

Will look into that as time permits. I'd guess an additional button on the breakpoints tab to access the exception breakpoint settings would be reasonable? Also, is it safe to assume these would be stored on a per-team basis, especially as they'll likely vary by source language?

comment:2 Changed 6 years ago by anevilyak

Blocked By: 9713 added

Adding blocker #9713 since the more complex variants of filtering by a set of exception types will likely require similar support as that ticket.

comment:3 in reply to:  1 ; Changed 6 years ago by bonefish

Replying to anevilyak:

I'd guess an additional button on the breakpoints tab to access the exception breakpoint settings would be reasonable?

Yeah, I don't really have a better idea at least.

Also, is it safe to assume these would be stored on a per-team basis, especially as they'll likely vary by source language?

Absolutely. Theoretically one setting per language would be needed. Though, IMO focusing on C++ only is fine ATM.

comment:4 Changed 6 years ago by anevilyak

Status: newin-progress

comment:5 Changed 6 years ago by anevilyak

For reference when it comes to handling the more complex parts of this, the Itanium exception ABI documentation. Still need to find similar documentation as concerns gcc2's ABI.

comment:6 Changed 6 years ago by anevilyak

A question: on gcc2, __throw is defined in libroot and nowhere else. However, on gcc4, we have __cxa_throw defined in both libroot and libstdc++. Is that deliberate, and if so, is there a particular rule as to when each will be called? In my quick tests it seems to consistently be the libstdc++ variant but that's hardly conclusive.

comment:7 in reply to:  6 Changed 6 years ago by bonefish

Replying to anevilyak:

A question: on gcc2, __throw is defined in libroot and nowhere else. However, on gcc4, we have __cxa_throw defined in both libroot and libstdc++. Is that deliberate, and if so, is there a particular rule as to when each will be called? In my quick tests it seems to consistently be the libstdc++ variant but that's hardly conclusive.

On gcc 2 the C++ runtime support comes with libgcc and is therefore built into libroot. On gcc 4 the C++ runtime support comes with libsupc++. Anything that is statically linked against libsupc++ (including libroot) may include parts of the C++ runtime. You'll probably have to handle the respective functions like any other function and set breakpoints in all instances. IIRC they are weak, so the runtime loader chooses exactly one of them to be used. I.e. in theory you could determine which one that is. Since there is no interface/API that provides this information, the only way to do that would be to emulate the runtime loader's algorithm. The first option seems easier to implement and more robust though.

comment:8 Changed 6 years ago by anevilyak

From some initial tests, trapping these functions does indeed work as expected as far as stopping the program when the exception is initially thrown. However, on gcc4.x this leads us to a problem due to some missing pieces in our x86 architecture support. Specifically, __cxa_throw appears to get built as a frameless function, as a consequence of which we don't actually wind up correctly unwinding to the location where the exception was actually thrown.

I can work around this by breaking at __cxa_allocate_exception instead, but ideally it'd be nice to have proper frameless support anyways, especially as this will also be needed for x86-64 in general, since the ABI default there is omit-frame-pointer. Can you shed any light as to what it would take to resolve http://haiku.it.su.se:8180/source/xref/src/apps/debugger/arch/x86/ArchitectureX86.cpp#309 ? I must admit at the moment my knowledge of that area is woefully inadequate.

comment:9 Changed 6 years ago by bonefish

I'm afraid I can't say much about unwinding frameless functions either. There's the trivial case, when the instruction pointer points to the first instruction of the function. Then you can easily unwind all registers. If the IP points anywhere else, you'll have to do quite a bit of work to even unwind esp and eip correctly. The only approach I can think of is, with the help of the disassembler, to simulate instruction execution starting at the IP continuing until you hit a "ret" instruction (at a conditional jump instruction e.g. try a depth-first search strategy). This way it should be possible to unwind esp and eip as well as all callee preserved registers. If the IP isn't too far into the function yet and before any jump target address, simulating instruction execution from the beginning to the IP would be possible as well.

Obviously this approach would not only work for frameless functions but also for functions with frame (in the absence of debug info).

comment:10 Changed 6 years ago by anevilyak

I was afraid the answer would be something in that vein, but secretly hoped there might be an easier answer :-). I guess in that case I'll go with the workaround for now then and leave a TODO note.

comment:11 in reply to:  3 Changed 6 years ago by anevilyak

Replying to bonefish:

Absolutely. Theoretically one setting per language would be needed. Though, IMO focusing on C++ only is fine ATM.

As far as this goes, since exception configuration would be team-wide, it seems the ideal approach would be to enumerate the languages used in the current team in order to find all possible languages that might need exception settings. Correspondingly, SourceLanguage would need to be extended to add the following capabilities:

  • indicate if the language in question supports exceptions and/or configuring them at all.
  • generate a language-specific exception settings configuration view.
  • provide functions that handle the corresponding exception breakpoint installation/removal.
  • exception settings persistence/restoration.

However, currently we only store references to the source languages on the source files that the functions reference, which is a bit unwieldy for quickly finding them all. My initial thought is to also keep a list of references on TeamDebugInfo itself in order to conveniently be able to discover/enumerate them quickly for purposes like the above, and anything else that might later need to access a particular language such as expression evaluation, though the latter's more likely to be in the context of a particular stack frame/function so that would implicitly know the correct language anyways I suppose.

As a starting point so we'll have something to work with initially, I have a simpler configuration window that just handles the C++ case for now, but the above pretty much outlines the direction I envision going to handle it properly in the long term. Feedback/design improvements would be appreciated.

comment:12 Changed 6 years ago by anevilyak

Status: in-progressassigned

Initial version of this implemented in hrev45764. The more advanced features will come as time permits, but the basic "stop when an exception is thrown" support for C++ is now there.

Note: See TracTickets for help on using tickets.