MachException.cpp revision 24943d2ee8bfaa7cf5893e4709143924157a5c1e
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 26extern "C" 27kern_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 37extern "C" 38kern_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 52extern "C" 53kern_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 68extern "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 77static MachException::Data *g_message = NULL; 78//static pthread_mutex_t g_message_mutex = PTHREAD_MUTEX_INITIALIZER; 79 80 81extern "C" 82kern_return_t 83catch_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 = " MACH_EXCEPTION_DATA_FMT_HEX ", exc_data_count = %d)", 99 __FUNCTION__, 100 exc_port, 101 exc_type, MachException::Name(exc_type), 102 exc_data, 103 exc_data_count); 104 } 105 return KERN_FAILURE; 106} 107 108extern "C" 109kern_return_t 110catch_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 kern_return_t kret; 126 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 127 { 128 DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", 129 __FUNCTION__, 130 exc_port, 131 thread_port, 132 task_port, 133 exc_type, MachException::Name(exc_type), 134 exc_data_count, 135 exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, 136 exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); 137 } 138 kret = mach_port_deallocate (mach_task_self (), task_port); 139 kret = mach_port_deallocate (mach_task_self (), thread_port); 140 141 return KERN_FAILURE; 142} 143 144extern "C" 145kern_return_t 146catch_mach_exception_raise 147( 148 mach_port_t exc_port, 149 mach_port_t thread_port, 150 mach_port_t task_port, 151 exception_type_t exc_type, 152 mach_exception_data_t exc_data, 153 mach_msg_type_number_t exc_data_count) 154{ 155 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 156 { 157 DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = 0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { " MACH_EXCEPTION_DATA_FMT_HEX ", " MACH_EXCEPTION_DATA_FMT_HEX " })", 158 __FUNCTION__, 159 exc_port, 160 thread_port, 161 task_port, 162 exc_type, MachException::Name(exc_type), 163 exc_data_count, 164 exc_data_count > 0 ? exc_data[0] : 0xBADDBADD, 165 exc_data_count > 1 ? exc_data[1] : 0xBADDBADD); 166 } 167 168 g_message->task_port = task_port; 169 g_message->thread_port = thread_port; 170 g_message->exc_type = exc_type; 171 g_message->exc_data.resize(exc_data_count); 172 ::memcpy (&g_message->exc_data[0], exc_data, g_message->exc_data.size() * sizeof (mach_exception_data_type_t)); 173 return KERN_SUCCESS; 174} 175 176 177void 178MachException::Message::Dump() const 179{ 180 DNBLogThreadedIf(LOG_EXCEPTIONS, 181 " exc_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx } ", 182 exc_msg.hdr.msgh_bits, 183 exc_msg.hdr.msgh_size, 184 exc_msg.hdr.msgh_remote_port, 185 exc_msg.hdr.msgh_local_port, 186 exc_msg.hdr.msgh_reserved, 187 exc_msg.hdr.msgh_id); 188 189 DNBLogThreadedIf(LOG_EXCEPTIONS, 190 "reply_msg { bits = 0x%8.8lx size = 0x%8.8lx remote-port = 0x%8.8lx local-port = 0x%8.8lx reserved = 0x%8.8lx id = 0x%8.8lx }", 191 reply_msg.hdr.msgh_bits, 192 reply_msg.hdr.msgh_size, 193 reply_msg.hdr.msgh_remote_port, 194 reply_msg.hdr.msgh_local_port, 195 reply_msg.hdr.msgh_reserved, 196 reply_msg.hdr.msgh_id); 197 198 state.Dump(); 199} 200 201bool 202MachException::Data::GetStopInfo(struct DNBThreadStopInfo *stop_info) const 203{ 204 // Zero out the structure. 205 memset(stop_info, 0, sizeof(struct DNBThreadStopInfo)); 206 // We always stop with a mach exceptions 207 stop_info->reason = eStopTypeException; 208 // Save the EXC_XXXX exception type 209 stop_info->details.exception.type = exc_type; 210 211 // Fill in a text description 212 const char * exc_name = MachException::Name(exc_type); 213 char *desc = stop_info->description; 214 const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH; 215 if (exc_name) 216 desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s", exc_name); 217 else 218 desc += snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i", exc_type); 219 220 stop_info->details.exception.data_count = exc_data.size(); 221 222 int soft_signal = SoftSignal(); 223 if (soft_signal) 224 { 225 if (desc < end_desc) 226 { 227 const char *sig_str = SysSignal::Name(soft_signal); 228 desc += snprintf(desc, end_desc - desc, " EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal, sig_str ? sig_str : "unknown signal"); 229 } 230 } 231 else 232 { 233 // No special disassembly for exception data, just 234 size_t idx; 235 if (desc < end_desc) 236 { 237 desc += snprintf(desc, end_desc - desc, " data[%zu] = {", stop_info->details.exception.data_count); 238 239 for (idx = 0; desc < end_desc && idx < stop_info->details.exception.data_count; ++idx) 240 desc += snprintf(desc, end_desc - desc, MACH_EXCEPTION_DATA_FMT_MINHEX "%c", exc_data[idx], ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ',')); 241 } 242 } 243 244 // Copy the exception data 245 size_t i; 246 for (i=0; i<stop_info->details.exception.data_count; i++) 247 stop_info->details.exception.data[i] = exc_data[i]; 248 249 return true; 250} 251 252 253void 254MachException::Data::DumpStopReason() const 255{ 256 int soft_signal = SoftSignal(); 257 if (soft_signal) 258 { 259 const char *signal_str = SysSignal::Name(soft_signal); 260 if (signal_str) 261 DNBLog("signal(%s)", signal_str); 262 else 263 DNBLog("signal(%i)", soft_signal); 264 return; 265 } 266 DNBLog("%s", Name(exc_type)); 267} 268 269kern_return_t 270MachException::Message::Receive(mach_port_t port, mach_msg_option_t options, mach_msg_timeout_t timeout, mach_port_t notify_port) 271{ 272 DNBError err; 273 const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS); 274 mach_msg_timeout_t mach_msg_timeout = options & MACH_RCV_TIMEOUT ? timeout : 0; 275 if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) 276 { 277 // Dump this log message if we have no timeout in case it never returns 278 DNBLogThreaded("::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)", 279 exc_msg.hdr.msgh_bits, 280 exc_msg.hdr.msgh_size, 281 exc_msg.hdr.msgh_remote_port, 282 exc_msg.hdr.msgh_local_port, 283 exc_msg.hdr.msgh_reserved, 284 exc_msg.hdr.msgh_id, 285 options, 286 0, 287 sizeof (exc_msg.data), 288 port, 289 mach_msg_timeout, 290 notify_port); 291 } 292 293 err = ::mach_msg (&exc_msg.hdr, 294 options, // options 295 0, // Send size 296 sizeof (exc_msg.data), // Receive size 297 port, // exception port to watch for exception on 298 mach_msg_timeout, // timeout in msec (obeyed only if MACH_RCV_TIMEOUT is ORed into the options parameter) 299 notify_port); 300 301 // Dump any errors we get 302 if (log_exceptions) 303 { 304 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)", 305 exc_msg.hdr.msgh_bits, 306 exc_msg.hdr.msgh_size, 307 exc_msg.hdr.msgh_remote_port, 308 exc_msg.hdr.msgh_local_port, 309 exc_msg.hdr.msgh_reserved, 310 exc_msg.hdr.msgh_id, 311 options, 312 0, 313 sizeof (exc_msg.data), 314 port, 315 mach_msg_timeout, 316 notify_port); 317 } 318 return err.Error(); 319} 320 321bool 322MachException::Message::CatchExceptionRaise() 323{ 324 bool success = false; 325 // locker will keep a mutex locked until it goes out of scope 326// PThreadMutex::Locker locker(&g_message_mutex); 327 // DNBLogThreaded("calling mach_exc_server"); 328 g_message = &state; 329 // The exc_server function is the MIG generated server handling function 330 // to handle messages from the kernel relating to the occurrence of an 331 // exception in a thread. Such messages are delivered to the exception port 332 // set via thread_set_exception_ports or task_set_exception_ports. When an 333 // exception occurs in a thread, the thread sends an exception message to 334 // its exception port, blocking in the kernel waiting for the receipt of a 335 // reply. The exc_server function performs all necessary argument handling 336 // for this kernel message and calls catch_exception_raise, 337 // catch_exception_raise_state or catch_exception_raise_state_identity, 338 // which should handle the exception. If the called routine returns 339 // KERN_SUCCESS, a reply message will be sent, allowing the thread to 340 // continue from the point of the exception; otherwise, no reply message 341 // is sent and the called routine must have dealt with the exception 342 // thread directly. 343 if (mach_exc_server (&exc_msg.hdr, &reply_msg.hdr)) 344 { 345 success = true; 346 } 347 else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 348 { 349 DNBLogThreaded("mach_exc_server returned zero..."); 350 } 351 g_message = NULL; 352 return success; 353} 354 355 356 357kern_return_t 358MachException::Message::Reply(MachProcess *process, int signal) 359{ 360 // Reply to the exception... 361 DNBError err; 362 363 // If we had a soft signal, we need to update the thread first so it can 364 // continue without signaling 365 int soft_signal = state.SoftSignal(); 366 if (soft_signal) 367 { 368 int state_pid = -1; 369 if (process->Task().TaskPort() == state.task_port) 370 { 371 // This is our task, so we can update the signal to send to it 372 state_pid = process->ProcessID(); 373 soft_signal = signal; 374 } 375 else 376 { 377 err = ::pid_for_task(state.task_port, &state_pid); 378 } 379 380 assert (state_pid != -1); 381 if (state_pid != -1) 382 { 383 errno = 0; 384 if (::ptrace (PT_THUPDATE, state_pid, (caddr_t)state.thread_port, soft_signal) != 0) 385 err.SetError(errno, DNBError::POSIX); 386 else 387 err.Clear(); 388 389 if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) 390 err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = 0x%4.4x, signal = %i)", state_pid, state.thread_port, soft_signal); 391 } 392 } 393 394 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)", 395 reply_msg.hdr.msgh_bits, 396 reply_msg.hdr.msgh_size, 397 reply_msg.hdr.msgh_remote_port, 398 reply_msg.hdr.msgh_local_port, 399 reply_msg.hdr.msgh_reserved, 400 reply_msg.hdr.msgh_id, 401 MACH_SEND_MSG | MACH_SEND_INTERRUPT, 402 reply_msg.hdr.msgh_size, 403 0, 404 MACH_PORT_NULL, 405 MACH_MSG_TIMEOUT_NONE, 406 MACH_PORT_NULL); 407 408 err = ::mach_msg ( &reply_msg.hdr, 409 MACH_SEND_MSG | MACH_SEND_INTERRUPT, 410 reply_msg.hdr.msgh_size, 411 0, 412 MACH_PORT_NULL, 413 MACH_MSG_TIMEOUT_NONE, 414 MACH_PORT_NULL); 415 416 if (err.Fail()) 417 { 418 if (err.Error() == MACH_SEND_INTERRUPTED) 419 { 420 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 421 err.LogThreaded("::mach_msg() - send interrupted"); 422 // TODO: keep retrying to reply??? 423 } 424 else 425 { 426 if (state.task_port == process->Task().TaskPort()) 427 { 428 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 429 err.LogThreaded("::mach_msg() - failed (task)"); 430 abort (); 431 } 432 else 433 { 434 if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) 435 err.LogThreaded("::mach_msg() - failed (child of task)"); 436 } 437 } 438 } 439 440 return err.Error(); 441} 442 443 444void 445MachException::Data::Dump() const 446{ 447 const char *exc_type_name = MachException::Name(exc_type); 448 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 : "???"); 449 450 const size_t exc_data_count = exc_data.size(); 451 // Dump any special exception data contents 452 int soft_signal = SoftSignal(); 453 if (soft_signal != 0) 454 { 455 const char *sig_str = SysSignal::Name(soft_signal); 456 DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data: EXC_SOFT_SIGNAL (%i (%s))", soft_signal, sig_str ? sig_str : "unknown signal"); 457 } 458 else 459 { 460 // No special disassembly for this data, just dump the data 461 size_t idx; 462 for (idx = 0; idx < exc_data_count; ++idx) 463 { 464 DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%u]: " MACH_EXCEPTION_DATA_FMT_HEX, idx, exc_data[idx]); 465 } 466 } 467} 468 469 470kern_return_t 471MachException::PortInfo::Save (task_t task) 472{ 473 count = (sizeof (ports) / sizeof (ports[0])); 474 DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Save ( task = 0x%4.4x )", task); 475 DNBError err; 476 err = ::task_get_exception_ports (task, EXC_MASK_ALL, masks, &count, ports, behaviors, flavors); 477 if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) 478 err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, maskCnt => %u, ports, behaviors, flavors )", task, EXC_MASK_ALL, count); 479 if (err.Fail()) 480 count = 0; 481 return err.Error(); 482} 483 484kern_return_t 485MachException::PortInfo::Restore (task_t task) 486{ 487 DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, "MachException::PortInfo::Restore( task = 0x%4.4x )", task); 488 uint32_t i = 0; 489 DNBError err; 490 if (count > 0) 491 { 492 for (i = 0; i < count; i++) 493 { 494 err = ::task_set_exception_ports (task, masks[i], ports[i], behaviors[i], flavors[i]); 495 if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) 496 { 497 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]); 498 // Bail if we encounter any errors 499 } 500 501 if (err.Fail()) 502 break; 503 } 504 } 505 count = 0; 506 return err.Error(); 507} 508 509const char * 510MachException::Name(exception_type_t exc_type) 511{ 512 switch (exc_type) 513 { 514 case EXC_BAD_ACCESS: return "EXC_BAD_ACCESS"; 515 case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION"; 516 case EXC_ARITHMETIC: return "EXC_ARITHMETIC"; 517 case EXC_EMULATION: return "EXC_EMULATION"; 518 case EXC_SOFTWARE: return "EXC_SOFTWARE"; 519 case EXC_BREAKPOINT: return "EXC_BREAKPOINT"; 520 case EXC_SYSCALL: return "EXC_SYSCALL"; 521 case EXC_MACH_SYSCALL: return "EXC_MACH_SYSCALL"; 522 case EXC_RPC_ALERT: return "EXC_RPC_ALERT"; 523#ifdef EXC_CRASH 524 case EXC_CRASH: return "EXC_CRASH"; 525#endif 526 default: 527 break; 528 } 529 return NULL; 530} 531 532 533 534