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