Home | History | Annotate | Download | only in MacOSX
      1 //===-- MachException.cpp ---------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  Created by Greg Clayton on 6/18/07.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "MachException.h"
     15 #include "MachProcess.h"
     16 #include "DNB.h"
     17 #include "DNBError.h"
     18 #include <sys/types.h>
     19 #include "DNBLog.h"
     20 #include "PThreadMutex.h"
     21 #include "SysSignal.h"
     22 #include <errno.h>
     23 #include <sys/ptrace.h>
     24 
     25 // Routine mach_exception_raise
     26 extern "C"
     27 kern_return_t catch_mach_exception_raise
     28 (
     29     mach_port_t exception_port,
     30     mach_port_t thread,
     31     mach_port_t task,
     32     exception_type_t exception,
     33     mach_exception_data_t code,
     34     mach_msg_type_number_t codeCnt
     35 );
     36 
     37 extern "C"
     38 kern_return_t catch_mach_exception_raise_state
     39 (
     40     mach_port_t exception_port,
     41     exception_type_t exception,
     42     const mach_exception_data_t code,
     43     mach_msg_type_number_t codeCnt,
     44     int *flavor,
     45     const thread_state_t old_state,
     46     mach_msg_type_number_t old_stateCnt,
     47     thread_state_t new_state,
     48     mach_msg_type_number_t *new_stateCnt
     49 );
     50 
     51 // Routine mach_exception_raise_state_identity
     52 extern "C"
     53 kern_return_t catch_mach_exception_raise_state_identity
     54 (
     55     mach_port_t exception_port,
     56     mach_port_t thread,
     57     mach_port_t task,
     58     exception_type_t exception,
     59     mach_exception_data_t code,
     60     mach_msg_type_number_t codeCnt,
     61     int *flavor,
     62     thread_state_t old_state,
     63     mach_msg_type_number_t old_stateCnt,
     64     thread_state_t new_state,
     65     mach_msg_type_number_t *new_stateCnt
     66 );
     67 
     68 extern "C" boolean_t mach_exc_server(
     69         mach_msg_header_t *InHeadP,
     70         mach_msg_header_t *OutHeadP);
     71 
     72 // Any access to the g_message variable should be done by locking the
     73 // g_message_mutex first, using the g_message variable, then unlocking
     74 // the g_message_mutex. See MachException::Message::CatchExceptionRaise()
     75 // for sample code.
     76 
     77 static MachException::Data *g_message = NULL;
     78 //static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER;
     79 
     80 
     81 extern "C"
     82 kern_return_t
     83 catch_mach_exception_raise_state
     84 (
     85     mach_port_t                 exc_port,
     86     exception_type_t            exc_type,
     87     const mach_exception_data_t exc_data,
     88     mach_msg_type_number_t      exc_data_count,
     89     int *                       flavor,
     90     const thread_state_t        old_state,
     91     mach_msg_type_number_t      old_stateCnt,
     92     thread_state_t              new_state,
     93     mach_msg_type_number_t *    new_stateCnt
     94 )
     95 {
     96     if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
     97     {
     98         DNBLogThreaded ("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data = 0x%llx, exc_data_count = %d)",
     99                         __FUNCTION__,
    100                         exc_port,
    101                         exc_type, MachException::Name(exc_type),
    102                         (uint64_t)exc_data,
    103                         exc_data_count);
    104     }
    105     return KERN_FAILURE;
    106 }
    107 
    108 extern "C"
    109 kern_return_t
    110 catch_mach_exception_raise_state_identity
    111 (
    112     mach_port_t             exc_port,
    113     mach_port_t             thread_port,
    114     mach_port_t             task_port,
    115     exception_type_t        exc_type,
    116     mach_exception_data_t   exc_data,
    117     mach_msg_type_number_t  exc_data_count,
    118     int *                   flavor,
    119     thread_state_t          old_state,
    120     mach_msg_type_number_t  old_stateCnt,
    121     thread_state_t          new_state,
    122     mach_msg_type_number_t *new_stateCnt
    123 )
    124 {
    125     if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
    126     {
    127         DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, 0x%llx })",
    128             __FUNCTION__,
    129             exc_port,
    130             thread_port,
    131             task_port,
    132             exc_type, MachException::Name(exc_type),
    133             exc_data_count,
    134             (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
    135             (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
    136     }
    137     mach_port_deallocate (mach_task_self (), task_port);
    138     mach_port_deallocate (mach_task_self (), thread_port);
    139 
    140     return KERN_FAILURE;
    141 }
    142 
    143 extern "C"
    144 kern_return_t
    145 catch_mach_exception_raise
    146 (
    147     mach_port_t             exc_port,
    148     mach_port_t             thread_port,
    149     mach_port_t             task_port,
    150     exception_type_t        exc_type,
    151     mach_exception_data_t   exc_data,
    152     mach_msg_type_number_t  exc_data_count)
    153 {
    154     if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
    155     {
    156         DNBLogThreaded ("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, 0x%llx })",
    157                         __FUNCTION__,
    158                         exc_port,
    159                         thread_port,
    160                         task_port,
    161                         exc_type, MachException::Name(exc_type),
    162                         exc_data_count,
    163                         (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
    164                         (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
    165     }
    166 
    167     if (task_port == g_message->task_port)
    168     {
    169         g_message->task_port = task_port;
    170         g_message->thread_port = thread_port;
    171         g_message->exc_type = exc_type;
    172         g_message->exc_data.resize(exc_data_count);
    173         ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t));
    174         return KERN_SUCCESS;
    175     }
    176     return KERN_FAILURE;
    177 }
    178 
    179 
    180 void
    181 MachException::Message::Dump() const
    182 {
    183     DNBLogThreadedIf(LOG_EXCEPTIONS,
    184         "  exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = 0x%8.8x } ",
    185         exc_msg.hdr.msgh_bits,
    186         exc_msg.hdr.msgh_size,
    187         exc_msg.hdr.msgh_remote_port,
    188         exc_msg.hdr.msgh_local_port,
    189         exc_msg.hdr.msgh_reserved,
    190         exc_msg.hdr.msgh_id);
    191 
    192     DNBLogThreadedIf(LOG_EXCEPTIONS,
    193         "reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = 0x%8.8x }",
    194         reply_msg.hdr.msgh_bits,
    195         reply_msg.hdr.msgh_size,
    196         reply_msg.hdr.msgh_remote_port,
    197         reply_msg.hdr.msgh_local_port,
    198         reply_msg.hdr.msgh_reserved,
    199         reply_msg.hdr.msgh_id);
    200 
    201     state.Dump();
    202 }
    203 
    204 bool
    205 MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const
    206 {
    207     // Zero out the structure.
    208     memset(stop_info, 0, sizeof(struct DNBThreadStopInfo));
    209     // We always stop with a mach exceptions
    210     stop_info->reason = eStopTypeException;
    211     // Save the EXC_XXXX exception type
    212     stop_info->details.exception.type = exc_type;
    213 
    214     // Fill in a text description
    215     const char * exc_name = MachException::Name(exc_type);
    216     char *desc = stop_info->description;
    217     const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH;
    218     if (exc_name)
    219         desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name);
    220     else
    221         desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type);
    222 
    223     stop_info->details.exception.data_count = exc_data.size();
    224 
    225     int soft_signal = SoftSignal();
    226     if (soft_signal)
    227     {
    228         if (desc < end_desc)
    229         {
    230             const char *sig_str = SysSignal::Name(soft_signal);
    231             snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, sig_str ? sig_str : "unknown signal");
    232         }
    233     }
    234     else
    235     {
    236         // No special disassembly for exception data, just
    237         size_t idx;
    238         if (desc < end_desc)
    239         {
    240             desc += snprintf(desc, end_desc - desc, " data[%llu] = {", (uint64_t)stop_info->details.exception.data_count);
    241 
    242             for (idx = 0; desc < end_desc && idx < stop_info->details.exception.data_count; ++idx)
    243                 desc += snprintf(desc, end_desc - desc, "0x%llx%c", (uint64_t)exc_data[idx], ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
    244         }
    245     }
    246 
    247     // Copy the exception data
    248     size_t i;
    249     for (i=0; i<stop_info->details.exception.data_count; i++)
    250         stop_info->details.exception.data[i] = exc_data[i];
    251 
    252     return true;
    253 }
    254 
    255 
    256 void
    257 MachException::Data::DumpStopReason() const
    258 {
    259     int soft_signal = SoftSignal();
    260     if (soft_signal)
    261     {
    262         const char *signal_str = SysSignal::Name(soft_signal);
    263         if (signal_str)
    264             DNBLog("signal(%s)", signal_str);
    265         else
    266             DNBLog("signal(%i)", soft_signal);
    267         return;
    268     }
    269     DNBLog("%s", Name(exc_type));
    270 }
    271 
    272 kern_return_t
    273 MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port)
    274 {
    275     DNBError err;
    276     const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS);
    277     mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0;
    278     if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0))
    279     {
    280         // Dump this log message if we have no timeout in case it never returns
    281         DNBLogThreaded ("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = 0, rcv_size = %llu, rcv_name = %#x, timeout = %u, notify = %#x)",
    282                         exc_msg.hdr.msgh_bits,
    283                         exc_msg.hdr.msgh_size,
    284                         exc_msg.hdr.msgh_remote_port,
    285                         exc_msg.hdr.msgh_local_port,
    286                         exc_msg.hdr.msgh_reserved,
    287                         exc_msg.hdr.msgh_id,
    288                         options,
    289                         (uint64_t)sizeof (exc_msg.data),
    290                         port,
    291                         mach_msg_timeout,
    292                         notify_port);
    293     }
    294 
    295     err = ::mach_msg (&exc_msg.hdr,
    296                       options,                  // options
    297                       0,                        // Send size
    298                       sizeof (exc_msg.data),    // Receive size
    299                       port,                     // exception port to watch for exception on
    300                       mach_msg_timeout,         // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter)
    301                       notify_port);
    302 
    303     // Dump any errors we get
    304     if (log_exceptions)
    305     {
    306         err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
    307             exc_msg.hdr.msgh_bits,
    308             exc_msg.hdr.msgh_size,
    309             exc_msg.hdr.msgh_remote_port,
    310             exc_msg.hdr.msgh_local_port,
    311             exc_msg.hdr.msgh_reserved,
    312             exc_msg.hdr.msgh_id,
    313             options,
    314             0,
    315             sizeof (exc_msg.data),
    316             port,
    317             mach_msg_timeout,
    318             notify_port);
    319     }
    320     return err.Error();
    321 }
    322 
    323 bool
    324 MachException::Message::CatchExceptionRaise(task_t task)
    325 {
    326     bool success = false;
    327     // locker will keep a mutex locked until it goes out of scope
    328 //    PThreadMutex::Locker locker(&g_message_mutex);
    329     //    DNBLogThreaded("calling  mach_exc_server");
    330     state.task_port = task;
    331     g_message = &state;
    332     // The exc_server function is the MIG generated server handling function
    333     // to handle messages from the kernel relating to the occurrence of an
    334     // exception in a thread. Such messages are delivered to the exception port
    335     // set via thread_set_exception_ports or task_set_exception_ports. When an
    336     // exception occurs in a thread, the thread sends an exception message to
    337     // its exception port, blocking in the kernel waiting for the receipt of a
    338     // reply. The exc_server function performs all necessary argument handling
    339     // for this kernel message and calls catch_exception_raise,
    340     // catch_exception_raise_state or catch_exception_raise_state_identity,
    341     // which should handle the exception. If the called routine returns
    342     // KERN_SUCCESS, a reply message will be sent, allowing the thread to
    343     // continue from the point of the exception; otherwise, no reply message
    344     // is sent and the called routine must have dealt with the exception
    345     // thread directly.
    346     if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr))
    347     {
    348         success = true;
    349     }
    350     else if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
    351     {
    352         DNBLogThreaded("mach_exc_server returned zero...");
    353     }
    354     g_message = NULL;
    355     return success;
    356 }
    357 
    358 
    359 
    360 kern_return_t
    361 MachException::Message::Reply(MachProcess *process, int signal)
    362 {
    363     // Reply to the exception...
    364     DNBError err;
    365 
    366     // If we had a soft signal, we need to update the thread first so it can
    367     // continue without signaling
    368     int soft_signal = state.SoftSignal();
    369     if (soft_signal)
    370     {
    371         int state_pid = -1;
    372         if (process->Task().TaskPort() == state.task_port)
    373         {
    374             // This is our task, so we can update the signal to send to it
    375             state_pid = process->ProcessID();
    376             soft_signal = signal;
    377         }
    378         else
    379         {
    380             err = ::pid_for_task(state.task_port, &state_pid);
    381         }
    382 
    383         assert (state_pid != -1);
    384         if (state_pid != -1)
    385         {
    386             errno = 0;
    387             if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)((uintptr_t)state.thread_port), soft_signal) != 0)
    388                 err.SetError(errno, DNBError::POSIX);
    389             else
    390                 err.Clear();
    391 
    392             if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
    393                 err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, soft_signal);
    394         }
    395     }
    396 
    397     DNBLogThreadedIf(LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option = %#x, send_size = %u, rcv_size = %u, rcv_name = %#x, timeout = %u, notify = %#x)",
    398         reply_msg.hdr.msgh_bits,
    399         reply_msg.hdr.msgh_size,
    400         reply_msg.hdr.msgh_remote_port,
    401         reply_msg.hdr.msgh_local_port,
    402         reply_msg.hdr.msgh_reserved,
    403         reply_msg.hdr.msgh_id,
    404         MACH_SEND_MSG | MACH_SEND_INTERRUPT,
    405         reply_msg.hdr.msgh_size,
    406         0,
    407         MACH_PORT_NULL,
    408         MACH_MSG_TIMEOUT_NONE,
    409         MACH_PORT_NULL);
    410 
    411     err = ::mach_msg (  &reply_msg.hdr,
    412                         MACH_SEND_MSG | MACH_SEND_INTERRUPT,
    413                         reply_msg.hdr.msgh_size,
    414                         0,
    415                         MACH_PORT_NULL,
    416                         MACH_MSG_TIMEOUT_NONE,
    417                         MACH_PORT_NULL);
    418 
    419     if (err.Fail())
    420     {
    421         if (err.Error() == MACH_SEND_INTERRUPTED)
    422         {
    423             if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
    424                 err.LogThreaded("::mach_msg() - send interrupted");
    425             // TODO: keep retrying to reply???
    426         }
    427         else
    428         {
    429             if (state.task_port == process->Task().TaskPort())
    430             {
    431                 if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
    432                     err.LogThreaded("::mach_msg() - failed (task)");
    433                 abort ();
    434             }
    435             else
    436             {
    437                 if (DNBLogCheckLogBit(LOG_EXCEPTIONS))
    438                     err.LogThreaded("::mach_msg() - failed (child of task)");
    439             }
    440         }
    441     }
    442 
    443     return err.Error();
    444 }
    445 
    446 
    447 void
    448 MachException::Data::Dump() const
    449 {
    450     const char *exc_type_name = MachException::Name(exc_type);
    451     DNBLogThreadedIf(LOG_EXCEPTIONS, "    state { task_port = 0x%4.4x, thread_port =  0x%4.4x, exc_type = %i (%s) ...", task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???");
    452 
    453     const size_t exc_data_count = exc_data.size();
    454     // Dump any special exception data contents
    455     int soft_signal = SoftSignal();
    456     if (soft_signal != 0)
    457     {
    458         const char *sig_str = SysSignal::Name(soft_signal);
    459         DNBLogThreadedIf(LOG_EXCEPTIONS, "            exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal");
    460     }
    461     else
    462     {
    463         // No special disassembly for this data, just dump the data
    464         size_t idx;
    465         for (idx = 0; idx < exc_data_count; ++idx)
    466         {
    467             DNBLogThreadedIf(LOG_EXCEPTIONS, "            exc_data[%llu]: 0x%llx", (uint64_t)idx, (uint64_t)exc_data[idx]);
    468         }
    469     }
    470 }
    471 
    472 #define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS      | \
    473                            EXC_MASK_BAD_INSTRUCTION | \
    474                            EXC_MASK_ARITHMETIC      | \
    475                            EXC_MASK_EMULATION       | \
    476                            EXC_MASK_SOFTWARE        | \
    477                            EXC_MASK_BREAKPOINT      | \
    478                            EXC_MASK_SYSCALL         | \
    479                            EXC_MASK_MACH_SYSCALL    | \
    480                            EXC_MASK_RPC_ALERT       | \
    481                            EXC_MASK_MACHINE)
    482 
    483 // Don't listen for EXC_RESOURCE, it should really get handled by the system handler.
    484 
    485 #ifndef EXC_RESOURCE
    486 #define EXC_RESOURCE 11
    487 #endif
    488 
    489 #ifndef EXC_MASK_RESOURCE
    490 #define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
    491 #endif
    492 
    493 #define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE)
    494 
    495 kern_return_t
    496 MachException::PortInfo::Save (task_t task)
    497 {
    498     DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task);
    499     // Be careful to be able to have debugserver built on a newer OS than what
    500     // it is currently running on by being able to start with all exceptions
    501     // and back off to just what is supported on the current system
    502     DNBError err;
    503 
    504     mask = LLDB_EXC_MASK;
    505 
    506     count = (sizeof (ports) / sizeof (ports[0]));
    507     err = ::task_get_exception_ports (task, mask, masks, &count, ports, behaviors, flavors);
    508     if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
    509         err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, mask, count);
    510 
    511     if (err.Error() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL)
    512     {
    513         mask = PREV_EXC_MASK_ALL;
    514         count = (sizeof (ports) / sizeof (ports[0]));
    515         err = ::task_get_exception_ports (task, mask, masks, &count, ports, behaviors, flavors);
    516         if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
    517             err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, mask, count);
    518     }
    519     if (err.Fail())
    520     {
    521         mask = 0;
    522         count = 0;
    523     }
    524     return err.Error();
    525 }
    526 
    527 kern_return_t
    528 MachException::PortInfo::Restore (task_t task)
    529 {
    530     DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Restore( task = 0x%4.4x )", task);
    531     uint32_t i = 0;
    532     DNBError err;
    533     if (count > 0)
    534     {
    535         for (i = 0; i < count; i++)
    536         {
    537             err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]);
    538             if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
    539             {
    540                 err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, exception_mask = 0x%8.8x, new_port = 0x%4.4x, behavior = 0x%8.8x, new_flavor = 0x%8.8x )", task, masks[i], ports[i], behaviors[i], flavors[i]);
    541                 // Bail if we encounter any errors
    542             }
    543 
    544             if (err.Fail())
    545                 break;
    546         }
    547     }
    548     count = 0;
    549     return err.Error();
    550 }
    551 
    552 const char *
    553 MachException::Name(exception_type_t exc_type)
    554 {
    555     switch (exc_type)
    556     {
    557     case EXC_BAD_ACCESS:        return "EXC_BAD_ACCESS";
    558     case EXC_BAD_INSTRUCTION:   return "EXC_BAD_INSTRUCTION";
    559     case EXC_ARITHMETIC:        return "EXC_ARITHMETIC";
    560     case EXC_EMULATION:         return "EXC_EMULATION";
    561     case EXC_SOFTWARE:          return "EXC_SOFTWARE";
    562     case EXC_BREAKPOINT:        return "EXC_BREAKPOINT";
    563     case EXC_SYSCALL:           return "EXC_SYSCALL";
    564     case EXC_MACH_SYSCALL:      return "EXC_MACH_SYSCALL";
    565     case EXC_RPC_ALERT:         return "EXC_RPC_ALERT";
    566 #ifdef EXC_CRASH
    567     case EXC_CRASH:             return "EXC_CRASH";
    568 #endif
    569     default:
    570         break;
    571     }
    572     return NULL;
    573 }
    574 
    575 
    576 
    577