[lldb-dev] Not stopping on EXC_BAD_ACCESS

jingham at apple.com jingham at apple.com
Mon Oct 21 11:42:41 PDT 2013


On Oct 19, 2013, at 11:11 PM, Heath Borders <heath.borders at gmail.com> wrote:

> (Sorry, I just discovered this list, and I don't know how to properly jump into a thread I didn't receive in email. I tried to replicate quoting one-level deep.)
> 
> 
> I want to be notified when a __weak reference in one of my objects is about to be nil-ed. I accomplish this by allocating the object into a specific VM page and then marking the page as read-only. When ARC nils the __weak reference, I get an EXC_BAD_ACCESS, and I can mark the VM page as read-write, run my tear-down code while the object is still valid, and then return KERN_SUCCESS, which will allow ARC to continue on. However, as Richard described earlier, LLDB hangs when I set a breakpoint inside any code that isn't my exception handler when I use task_set_exception_ports.

If you were watching all the exception types on the task exception port, then it would make sense to me that we would hang trying to continue, since lldb uses EXC_BREAKPOINT for its own purposes and if it is not seeing that, it won't be able to run its execution logic.  But if you are only watching for the EXC_BAD_ACCESS exception then lldb it shouldn't care about this (besides that lldb won't ever see real crashes.)  

Anyway, check that you really are only registering for the EXC_BAD_ACCESS exception when you do the {task,thread}_set_exception_ports.  Or, see below, maybe this is happening because you aren't replying to the exception message?

> 
> You suggested using thread_set_exception_ports to fix this issue. I assume that thread_set_exception_ports only takes over exceptions that occur on the thread given to thread_set_exception_ports, and not for the whole task, like task_set_exception_ports does. Thus, I would need to call thread_set_exception_ports on every thread since I care about getting an EXC_BAD_ACCESS exception that occurs on any thread, right? My handler isn't called if I register for exceptions with a different thread.
> 
> Is there a way to be notified when a thread is created so I can subscribe to its messages?

Not that I am aware of.  There's no very good way to to watch for exceptions on threads you didn't create using the thread exception port, at least not one I know about.

> 
> Also, I still see LLDB hanging when a use thread_set_exception_ports. Am I doing something wrong? My code is below:

Is lldb hanging, or is your program hanging?  If lldb is hanging, can you file a bug with the backtrace of lldb (and a sample program if you can easily whip one up...)

If it is your program that is hanging make sure you are sending on the reply message that you get when your catch_exception_raise returns KERN_SUCCESS.  Maybe that is what mach_msg_server does, I've never used that function so I don't know.  In lldb we call mach_exc_server to trigger the call to catch_exception_raise and that returns a reply right that you have to send on to the appropriate exception port for the process to continue.  If you don't do that your program won't make progress.  As I said, that may be already handled in mach_msg_server, I don't know, not having done it that way, but that's the only thing I can think of that might hang you up.  You can check out how lldb does this here:

http://llvm.org/svn/llvm-project/lldb/trunk/tools/debugserver/source/MacOSX/MachException.cpp

Jim


> 
> kern_return_t catch_exception_raise(mach_port_t exception_port,
> 
>                                     mach_port_t thread,
> 
>                                     mach_port_t task,
>                                     exception_type_t exception,
>                                     exception_data_t code_vector,
>                                     mach_msg_type_number_t code_count) {
> 
>     x86_exception_state32_t x86_exception_state32;
> 
>     mach_msg_type_number_t sc = x86_EXCEPTION_STATE32_COUNT;
>     
>     thread_get_state(thread,
>                              x86_EXCEPTION_STATE32,
>                              (thread_state_t)&x86_exception_state32,
> 
>                              &sc);
> 
> 
>     // pull fault address from x86_exception_state32
>     // check that it is an expected address
>     // make the page read-write
>     // do cleanup
> 
>     return KERN_SUCCESS;  
> }
> 
> void *exception_handler(void *arg) {
> 
>     extern boolean_t exc_server();
>     mach_port_t port = (mach_port_t) arg;
>     mach_msg_server(exc_server, 2048, port, 0);
>     abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns)
> 
> }
> 
> // Does this need to be called on every thread?
> // I assume exception_port can be reused. Is this true?
> void setup_mach_exception_port() {
> 
>     static mach_port_t exception_port = MACH_PORT_NULL;
>     mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
>     mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
> 
>     // using task_set_exception_ports causes LLDB to hang...
>     // ...whenever it hits breakpoint outside of catch_exception_raise
> 
>     // 
> task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
>      thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
>     pthread_t returned_thread;
> 
>     pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port);
> }
> 
> On Jul 8, 2013, at 12:41 PM, Greg Clayton <gclayton at apple.com> wrote: 
> Not many people actually take over their own mach exception ports, so this isn't something that hits a lot of people. It mainly hits the GC folks and the Runtime (Java) folks that need to intercept NULL derefs.
> We do know about the issue and have plans to address this in the future.
> The solution that works right now is to take over EXC_BAD_ACCESS on the _thread_ mach port. GDB and LLDB take over the task exception ports but we leave the thread exception ports alone. The thread mach ports will get the exception first, and if not handled, will pass it along to the task exception ports. Depending on your architecture, you might easily be able to do this, or it might be difficult.
> Greg Clayton
> 
> 
> -Heath Borders
> heath.borders at gmail.com
> Twitter: heathborders
> http://heath-tech.blogspot.com
> _______________________________________________
> lldb-dev mailing list
> lldb-dev at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev




More information about the lldb-dev mailing list