break on exceptions/windows

How do I break on (first chance) exceptions on Windows?

Not possible currently, although it wouldn’t be too hard to add. Would be a welcome feature if you are interested

I'm (obviously?) interested. But wouldn't know where to start.

Take a look at ProcessWindowsLive.cpp in Plugins/Process/Windows. There’s a function called ProcessWindowsLive::OnDebugException. If you’re working in a fork and you don’t intend to upstream any changes, you could just modify the default case of the switch statement there to not return ExceptionResult::SendToApplication.

If you wanted to upstream something, you’d probably want a way to specify what types of exceptions to break on. For this you’d need to implement a new command so that you could do something like “break set --exception 0xC0000005” and pass that information to the ProcessWindowsLive plugin somehow, so that it could decide when to break and when to pass it on to the application for a second chance.

You should talk to Jim Ingham on this. We have special exception breakpoints that we did for Swift and you will want to follow the same methodology. I am not sure what the methodology is so I'm CC'ing Jim so can can comment.

Greg

The exception breakpoints Greg is talking about are language exceptions (C++ throws, Swift Errors and the like.)

I don't know what kind of exception you are talking about here, but at least from a command interface standpoint, it would be good to keep alike things that actually are alike, but only if they ARE actually alike.

Jim

Windows works a little differently. Windows has the notion of a Windows exception, which the best way I can describe it is like a signal. But it’s an arbitrary 32-bit value, and there are hundreds, if not thousands of different values. here’s a few:

0x40010005 Ctrl+C
0x80000003 Breakpoint (e.g. int 3)
0x80000004 Single Step
0xC0000005 Access violation
0xC000001D Illegal Instruction
0xC0000095 Integer overflow
0xE06D7363 C++ Exception

Note the last one. It’s interesting, because it illustrates that on Windows, C++ exceptions are just Windows exceptions with a different code. And if you were to implement some other programming language such as swift on Windows, you would probably even do it the same way, by finding an unused exception code and raising it with your language-specific exception context.

When any of these happen in the inferior, the debugger gets a notification (called a first-chance exception), and the debugger can decide what to do, such as break into the debugger, or ignore it and pass it on to the application

It’s possible this makes more sense as a windows specific debugger command, or perhaps a windows specific subcommand of the “break” command that is only available if the selected target is windows.

Existing Windows debuggers allow exception breakpoints of this nature through a consistent interface, with the ability to drill down into different types of exceptions. What I mean is, you can set the debugger to stop on all access violations or all C++ exceptions, but you can get more advanced for C++ exceptions and set an exception when a specific type is thrown (like a std::string, for example). The debugger would implement this by installing a 0xE06D7363 exception breakpoint, and ignoring any where the type wasn’t std::string (by analyzing the context record).

So, there is at least one aspect of this work that shares some behavior with how C++ language exceptions might work with clang on non-Windows platforms. Being able to say “break when a std::string is thrown” is not OS-specific and very useful.

But the other aspect is not, and there’s not an obvious way to present a consistent interface (e.g. command line command) to the OS-independent functionality across platforms, while also presenting a consistent interface to the OS specific functionality on Windows.

Interesting.

For the other windows exceptions, could we do something abstract like:

(lldb) break set --platform <data>

(-P for short) to set a "platform breakpoint", where "data" is a string that the current platform will understand, but the breakpoint machinery doesn't have to.

The way breakpoints work internally is that somebody provides a "BreakpointResolver" subclass which gets called when anything interesting happens in the program (rerun, shared library load, etc). It's the BreakpointResolver's job to add locations to the breakpoint, describe itself and the like. But this is a fairly abstract interface, and it wouldn't be hard to have a Platform function that got passed "data" and returned a breakpoint resolver to turn on the watch for these exceptions.

Then when the breakpoint gets set, the model is Breakpoints have "BreakpointLocations" which each add one place a stop might occur: a "BreakpointSite". The division between Sites & Locations is because two logically different Locations could map to the same physical Site. Then the Sites are used at the lowest level to map a stop reason back into the breakpoint(s) that caused it. To date, the only BreakpointSites are PC based, so the BreakpointList gets asked "is there a site at this PC". But that all happens in the process plugins, so it wouldn't be hard to map other stop reasons to particular sites. The lower layers of the process (e.g. ProcessGDBRemote) figure out which site maps to the stop reason, and makes a StopInfoBreakpoint with that BreakpointSite. And after that the Site -> Location -> Breakpoint logic is done w/o much care how the Site actually works.

WRT language exceptions in specific, in lldb you say:

(lldb) break set -E c++

to break on C++ exception throws. You would say:

(lldb) break set -E c++ -O <OBJECT_NAME>

to restrict the exception throw to a particular object type, except I haven't implemented this for anything but Swift errors yet, but it wouldn't be hard to do that. So regardless of what we do with the other Windows exceptions, we should implement the language exceptions consistently this way at the command line level just to keep things consistent. But again, once we're able to hand out "BreakpointResolverWindowsExceptions" in general, we could create them on Windows for the C++ ABI as well (another reason you'll probably want a Windows C++ language runtime since it's the itanium ABI that does this job on other platforms. The object filtering is mostly runtime ABI work - to figure out the thrown exception from the throw site. But that's just some ABI function that the general matching code would call.

This would be some work, for sure, but I don't think it would be terribly hard to do.

Jim

It seems like we already have some precedent for conditional command arguments. For example:

(lldb) help platform process list

-u ( --uid )
[POSIX] Find processes that have a matching user ID.

So on Windows this argument doesn’t make sense. Could we make an argument that is conditional on the target rather than the host? Then, for example, you could have something like this:

(lldb) help break set

–code ( --code )
[Windows Target] Break when the exception with code is raised.

How to plumb this to the ProcessWindows plugin is an open question, but should be mostly mechanical.

One easy way to abstract this would be to allow a "--exception-code" option:

(lldb) breakpoint set --exception-code 0x40010005

This would pass through to the platform and ask each platform to make a breakpoint using the platform specific code.

We could also add a similar option that names the exception:

(lldb) breakpoint set --exception-name "Integer overflow"

Where the name would be passed on to the platform and allow the platform to set the exception breakpoint as it sees fit? If the option was used incorrectly we could query the current platform for all exception names and display them and probably add a command line command that could list all exception codes and names so that it would be discoverable.

Greg

It seems like we already have some precedent for conditional command arguments. For example:

(lldb) help platform process list
...
       -u <unsigned-integer> ( --uid <unsigned-integer> )
            [POSIX] Find processes that have a matching user ID.

So on Windows this argument doesn't make sense. Could we make an argument that is conditional on the *target* rather than the host? Then, for example, you could have something like this:

(lldb) help break set
...
       --code <hex-integer> ( --code <hex-integer> )
            [Windows Target] Break when the exception with code <code> is raised.

How to plumb this to the ProcessWindows plugin is an open question, but should be mostly mechanical.

This is like my suggestion of:

(lldb) breakpoint set --exception-code 0x40010005

The code can be passed to the current Platform along with the current target:

Error Platform::SetExceptionBreakpointWithExceptionCode (lldb_private::Target *target, uint64_t exception_code);

The process can be extracted from the target when the breakpoint needs to be resolved.

There should be a way then to do a "break on every exception", instead of just 1 specific code.

and some way for the api to get the payload (which can have a variable number of parameters)

It seems like we already have some precedent for conditional command arguments. For example:

(lldb) help platform process list
...
       -u <unsigned-integer> ( --uid <unsigned-integer> )
            [POSIX] Find processes that have a matching user ID.

So on Windows this argument doesn't make sense. Could we make an argument that is conditional on the *target* rather than the host? Then, for example, you could have something like this:

(lldb) help break set
...
       --code <hex-integer> ( --code <hex-integer> )
            [Windows Target] Break when the exception with code <code> is raised.

How to plumb this to the ProcessWindows plugin is an open question, but should be mostly mechanical.

This is like my suggestion of:

(lldb) breakpoint set --exception-code 0x40010005

The code can be passed to the current Platform along with the current target:

Error Platform::SetExceptionBreakpointWithExceptionCode (lldb_private::Target *target, uint64_t exception_code);

The process can be extracted from the target when the breakpoint needs to be resolved.

There should be a way then to do a "break on every exception", instead of just 1 specific code.

That would be easy with the --exception-name:

(lldb) breakpoint set --exception-name=all

and some way for the api to get the payload (which can have a variable number of parameters)

What are you thinking here? Example?

Yes, that's why I prefer a more abstract command interface than trying to be too specific about some abstract breakpoint. So you'd just have:

Error
Platform::SetPlatformBreakpoint(lldb_private::Target *target, const char *data);

Then this can have any meaning that it needs to. The other way to structure this is:

break set -P -key "KEY" -value "VALUE"

Jim

I have a frontend language (and I imagine lots of others have one) where I can throw exceptions. Windows has no predefined way of how an exception object is formatted, you give it a list of pointer sized ints and a count, and that's what it fills the exception object with, for example I pass:
[0] Return address
[1] frame pointer
[2] Class instance of the exception object
with my custom exception code.

msvc does the same with completely different values. Other languages will have their own values. When using the api I would want access to the code (To know of it's mine, these are the cods Zachery Turner mentioned) and the payload (to get access to the object) so that I show what exception occurred and turn it into a string representation.

There should be a way then to do a "break on every exception", instead of just 1 specific code.

That would be easy with the --exception-name:

(lldb) breakpoint set --exception-name=all

and some way for the api to get the payload (which can have a variable number of parameters)

What are you thinking here? Example?

I have a frontend language (and I imagine lots of others have one) where I can throw exceptions. Windows has no predefined way of how an exception object is formatted, you give it a list of pointer sized ints and a count, and that's what it fills the exception object with, for example I pass:
[0] Return address
[1] frame pointer
[2] Class instance of the exception object
with my custom exception code.

msvc does the same with completely different values. Other languages will have their own values. When using the api I would want access to the code (To know of it's mine, these are the cods Zachery Turner mentioned) and the payload (to get access to the object) so that I show what exception occurred and turn it into a string representation.

Language exceptions are a general class of thing, and those should all be treated similarly if possible.

The way you would do this for your new language is to would add a LanguageRuntime for your language, and that would implement CreateExceptionBreakpoint using a BreakpointResolverWindowsException for the correct exception code. The Object filtering is done using "BreakpointPreconditions", which are a way for a breakpoint to stick some code that gets called right when the breakpoint is hit before any of the other ShouldStop machinery takes over. When you make the breakpoint you would pass the "thrown object specifier" to the breakpoint, which would make an appropriate precondition that would know how to dig out the object and compare.

So you would see:

(lldb) breakpoint set -E MyNewLanguage -O MyObjectName

This is currently only implemented in Swift, though the needed infrastructure is present in the llvm tree.

If there are other things you want to do that aren't generalizable, then we should treat them as opaque data that gets passed to the platform and it will know how to make the resolver & if needed precondition to do the filtering.

Jim

I really would rather avoid the key/value thing. I prefer the --exception-name and --exception-code and have the platform handle it. Seems cleaner.

Greg

Another option would be to have sub-sub commands. Already when you mix so many options together, lots of the options don’t make sense with each other. What about

break set windows --exc-code=0xC0000005

This way all the windows specific stuff is wrapped up behind another subcommand, and you don’t have to worry about consistency with other platforms’ interfaces.

We could add a "platform breakpoint set" command as a new stand alone breakpoint mechanism and avoid messing with the "breakpoint set" command at all.

(lldb) platform breakpoint set ...

This would be passed to the current lldb_private::Platform plug-in for it to parse as needed. Each platform can have their own options that are completely custom.

If we're going this far, then we should just add a "catch" command, and have the platforms be able to add "catchable" things. For instance, you could catch shared library loads, you could catch fork & exec, maybe IPC message sends and Windows exceptions. Seems like they fit better in this model than as breakpoints.

Jim