Index: snapshot/exception_snapshot_mac.cc |
diff --git a/snapshot/exception_snapshot_mac.cc b/snapshot/exception_snapshot_mac.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4968ff2c48b78b66b2e7b081013a5e15ee3c390a |
--- /dev/null |
+++ b/snapshot/exception_snapshot_mac.cc |
@@ -0,0 +1,175 @@ |
+// Copyright 2014 The Crashpad Authors. All rights reserved. |
+// |
+// Licensed under the Apache License, Version 2.0 (the "License"); |
+// you may not use this file except in compliance with the License. |
+// You may obtain a copy of the License at |
+// |
+// http://www.apache.org/licenses/LICENSE-2.0 |
+// |
+// Unless required by applicable law or agreed to in writing, software |
+// distributed under the License is distributed on an "AS IS" BASIS, |
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+// See the License for the specific language governing permissions and |
+// limitations under the License. |
+ |
+#include "snapshot/exception_snapshot_mac.h" |
+ |
+#include "base/logging.h" |
+#include "base/strings/stringprintf.h" |
+#include "snapshot/cpu_context_mac.h" |
+#include "util/mac/process_reader.h" |
+#include "util/mach/exc_server_variants.h" |
+#include "util/numeric/safe_assignment.h" |
+ |
+namespace crashpad { |
+namespace internal { |
+ |
+ExceptionSnapshotMac::ExceptionSnapshotMac() |
+ : ExceptionSnapshot(), |
+ context_union_(), |
+ context_(), |
+ codes_(), |
+ thread_id_(0), |
+ exception_address_(0), |
+ exception_(0), |
+ exception_code_0_(0), |
+ initialized_() { |
+} |
+ |
+ExceptionSnapshotMac::~ExceptionSnapshotMac() { |
+} |
+ |
+bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, |
+ thread_t exception_thread, |
+ exception_type_t exception, |
+ const mach_exception_data_type_t* code, |
+ mach_msg_type_number_t code_count, |
+ thread_state_flavor_t flavor, |
+ const natural_t* state, |
+ mach_msg_type_number_t state_count) { |
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
+ |
+ codes_.push_back(exception); |
+ for (mach_msg_type_number_t code_index = 0; |
+ code_index < code_count; |
+ ++code_index) { |
+ codes_.push_back(code[code_index]); |
+ } |
+ |
+ exception_ = exception; |
+ mach_exception_code_t exception_code_0 = code[0]; |
+ |
+ if (exception_ == EXC_CRASH) { |
+ exception_ = ExcCrashRecoverOriginalException( |
+ exception_code_0, &exception_code_0, NULL); |
+ } |
+ |
+ // ExceptionInfo() returns code[0] in a 32-bit field. This shouldn’t be a |
+ // problem because code[0]’s values never exceed 32 bits. Only code[1] is ever |
+ // expected to be that wide. |
+ if (!AssignIfInRange(&exception_code_0_, exception_code_0)) { |
+ LOG(WARNING) |
+ << base::StringPrintf("exception_code_0 0x%llx out of range", |
+ exception_code_0); |
+ return false; |
+ } |
+ |
+ const ProcessReader::Thread* thread = NULL; |
+ for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) { |
+ if (exception_thread == loop_thread.port) { |
+ thread = &loop_thread; |
+ break; |
+ } |
+ } |
+ |
+ if (!thread) { |
+ LOG(WARNING) << "exception_thread not found in task"; |
+ return false; |
+ } |
+ |
+ thread_id_ = thread->id; |
+ |
+ // Normally, the exception address is present in code[1] for EXC_BAD_ACCESS |
+ // exceptions, but not for other types of exceptions. |
+ bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; |
+ |
+#if defined(ARCH_CPU_X86_FAMILY) |
+ if (process_reader->Is64Bit()) { |
+ context_.architecture = kCPUArchitectureX86_64; |
+ context_.x86_64 = &context_union_.x86_64; |
+ InitializeCPUContextX86_64(context_.x86_64, |
+ flavor, |
+ state, |
+ state_count, |
+ &thread->thread_context.t64, |
+ &thread->float_context.f64, |
+ &thread->debug_context.d64); |
+ } else { |
+ context_.architecture = kCPUArchitectureX86; |
+ context_.x86 = &context_union_.x86; |
+ InitializeCPUContextX86(context_.x86, |
+ flavor, |
+ state, |
+ state_count, |
+ &thread->thread_context.t32, |
+ &thread->float_context.f32, |
+ &thread->debug_context.d32); |
+ } |
+ |
+ // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate |
+ // that code[1] does not (or may not) carry the exception address: |
+ // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for |
+ // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE) |
+ // which collides with EXC_I386_BOUNDFLT (10.9.5 |
+ // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS |
+ // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c |
+ // user_page_fault_continue() and do contain the exception address in code[1]. |
+ if (exception_ == EXC_BAD_ACCESS && |
+ (exception_code_0_ == EXC_I386_GPFLT || |
+ exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { |
+ code_1_is_exception_address = false; |
+ } |
+#endif |
+ |
+ if (code_1_is_exception_address) { |
+ exception_address_ = code[1]; |
+ } else { |
+ exception_address_ = context_.InstructionPointer(); |
+ } |
+ |
+ INITIALIZATION_STATE_SET_VALID(initialized_); |
+ return true; |
+} |
+ |
+const CPUContext* ExceptionSnapshotMac::Context() const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ return &context_; |
+} |
+ |
+uint64_t ExceptionSnapshotMac::ThreadID() const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ return thread_id_; |
+} |
+ |
+uint32_t ExceptionSnapshotMac::Exception() const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ return exception_; |
+} |
+ |
+uint32_t ExceptionSnapshotMac::ExceptionInfo() const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ return exception_code_0_; |
+} |
+ |
+uint64_t ExceptionSnapshotMac::ExceptionAddress() const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ return exception_address_; |
+} |
+ |
+const std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ return codes_; |
+} |
+ |
+} // namespace internal |
+} // namespace crashpad |