| // Copyright 2020 The Crashpad Authors |
| // |
| // 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/ios/thread_snapshot_ios_intermediate_dump.h" |
| |
| #include "base/apple/mach_logging.h" |
| #include "snapshot/ios/intermediate_dump_reader_util.h" |
| #include "snapshot/mac/cpu_context_mac.h" |
| #include "util/ios/ios_intermediate_dump_data.h" |
| #include "util/ios/ios_intermediate_dump_list.h" |
| #include "util/ios/ios_intermediate_dump_map.h" |
| |
| #include <vector> |
| |
| namespace { |
| |
| std::vector<uint8_t> GenerateStackMemoryFromFrames(const uint64_t* frames, |
| const size_t frame_count) { |
| std::vector<uint8_t> stack_memory; |
| if (frame_count < 2) { |
| return stack_memory; |
| } |
| size_t pointer_size = sizeof(uintptr_t); |
| size_t frame_record_size = 2 * pointer_size; |
| size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size; |
| stack_memory.resize(stack_size); |
| uintptr_t sp = stack_size - pointer_size; |
| uintptr_t fp = 0; |
| uintptr_t lr = 0; |
| for (size_t current_frame = frame_count - 1; current_frame > 0; |
| --current_frame) { |
| memcpy(&stack_memory[0] + sp, &lr, sizeof(lr)); |
| sp -= pointer_size; |
| memcpy(&stack_memory[0] + sp, &fp, sizeof(fp)); |
| fp = sp; |
| sp -= pointer_size; |
| lr = frames[current_frame]; |
| } |
| |
| if (sp != 0) { |
| LOG(ERROR) << "kExpectedFinalSp should be 0, is " << sp; |
| } |
| if (fp != sizeof(uintptr_t)) { |
| LOG(ERROR) << "kExpectedFinalFp should be sizeof(uintptr_t), is " << fp; |
| } |
| if (lr != frames[1]) { |
| LOG(ERROR) << "lr should be " << frames[1] << ", is " << lr; |
| } |
| return stack_memory; |
| } |
| |
| } // namespace |
| namespace crashpad { |
| namespace internal { |
| |
| using Key = IntermediateDumpKey; |
| |
| ThreadSnapshotIOSIntermediateDump::ThreadSnapshotIOSIntermediateDump() |
| : ThreadSnapshot(), |
| #if defined(ARCH_CPU_X86_64) |
| context_x86_64_(), |
| #elif defined(ARCH_CPU_ARM64) |
| context_arm64_(), |
| #endif |
| context_(), |
| stack_(), |
| thread_name_(), |
| thread_id_(0), |
| thread_specific_data_address_(0), |
| suspend_count_(0), |
| priority_(0), |
| initialized_() { |
| #if defined(ARCH_CPU_X86_64) |
| context_.architecture = kCPUArchitectureX86_64; |
| context_.x86_64 = &context_x86_64_; |
| #elif defined(ARCH_CPU_ARM64) |
| context_.architecture = kCPUArchitectureARM64; |
| context_.arm64 = &context_arm64_; |
| #endif |
| } |
| |
| ThreadSnapshotIOSIntermediateDump::~ThreadSnapshotIOSIntermediateDump() {} |
| |
| bool ThreadSnapshotIOSIntermediateDump::Initialize( |
| const IOSIntermediateDumpMap* thread_data) { |
| INITIALIZATION_STATE_SET_INITIALIZING(initialized_); |
| |
| GetDataValueFromMap(thread_data, Key::kSuspendCount, &suspend_count_); |
| GetDataValueFromMap(thread_data, Key::kPriority, &priority_); |
| GetDataValueFromMap(thread_data, Key::kThreadID, &thread_id_); |
| GetDataValueFromMap( |
| thread_data, Key::kThreadDataAddress, &thread_specific_data_address_); |
| GetDataStringFromMap(thread_data, Key::kThreadName, &thread_name_); |
| |
| #if defined(ARCH_CPU_X86_64) |
| typedef x86_thread_state64_t thread_state_type; |
| typedef x86_float_state64_t float_state_type; |
| typedef x86_debug_state64_t debug_state_type; |
| #elif defined(ARCH_CPU_ARM64) |
| typedef arm_thread_state64_t thread_state_type; |
| typedef arm_neon_state64_t float_state_type; |
| typedef arm_debug_state64_t debug_state_type; |
| #endif |
| |
| thread_state_type thread_state; |
| float_state_type float_state; |
| debug_state_type debug_state; |
| |
| const IOSIntermediateDumpData* nsexception_frames = |
| thread_data->GetAsData(Key::kThreadUncaughtNSExceptionFrames); |
| const IOSIntermediateDumpData* thread_stack_data_dump = |
| thread_data->GetAsData(Key::kStackRegionData); |
| if (nsexception_frames && thread_stack_data_dump) { |
| LOG(ERROR) << "Unexpected thread with kStackRegionData and " |
| << "kThreadUncaughtNSExceptionFrames, using kStackRegionData"; |
| } |
| if (thread_stack_data_dump) { |
| vm_address_t stack_region_address; |
| GetDataValueFromMap( |
| thread_data, Key::kStackRegionAddress, &stack_region_address); |
| |
| const std::vector<uint8_t>& bytes = thread_stack_data_dump->bytes(); |
| const vm_address_t stack_region_data = |
| reinterpret_cast<const vm_address_t>(bytes.data()); |
| vm_size_t stack_region_size = bytes.size(); |
| stack_.Initialize( |
| stack_region_address, stack_region_data, stack_region_size); |
| } else if (nsexception_frames) { |
| const std::vector<uint8_t>& bytes = nsexception_frames->bytes(); |
| const uint64_t* frames = reinterpret_cast<const uint64_t*>(bytes.data()); |
| size_t frame_count = bytes.size() / sizeof(uint64_t); |
| exception_stack_memory_ = |
| GenerateStackMemoryFromFrames(frames, frame_count); |
| vm_address_t stack_memory_addr = |
| !exception_stack_memory_.empty() |
| ? reinterpret_cast<vm_address_t>(&exception_stack_memory_[0]) |
| : 0; |
| stack_.Initialize(0, stack_memory_addr, exception_stack_memory_.size()); |
| } else { |
| stack_.Initialize(0, 0, 0); |
| } |
| |
| if (GetDataValueFromMap(thread_data, Key::kThreadState, &thread_state) && |
| GetDataValueFromMap(thread_data, Key::kFloatState, &float_state) && |
| GetDataValueFromMap(thread_data, Key::kDebugState, &debug_state)) { |
| #if defined(ARCH_CPU_X86_64) |
| InitializeCPUContextX86_64(&context_x86_64_, |
| THREAD_STATE_NONE, |
| nullptr, |
| 0, |
| &thread_state, |
| &float_state, |
| &debug_state); |
| #elif defined(ARCH_CPU_ARM64) |
| InitializeCPUContextARM64(&context_arm64_, |
| THREAD_STATE_NONE, |
| nullptr, |
| 0, |
| &thread_state, |
| &float_state, |
| &debug_state); |
| |
| #else |
| #error Port to your CPU architecture |
| #endif |
| } |
| const IOSIntermediateDumpList* thread_context_memory_regions = |
| GetListFromMap(thread_data, Key::kThreadContextMemoryRegions); |
| if (thread_context_memory_regions) { |
| for (auto& region : *thread_context_memory_regions) { |
| vm_address_t address; |
| const IOSIntermediateDumpData* region_data = |
| region->GetAsData(Key::kThreadContextMemoryRegionData); |
| if (!region_data) |
| continue; |
| if (GetDataValueFromMap( |
| region.get(), Key::kThreadContextMemoryRegionAddress, &address)) { |
| const std::vector<uint8_t>& bytes = region_data->bytes(); |
| vm_size_t data_size = bytes.size(); |
| if (data_size == 0) |
| continue; |
| |
| const vm_address_t data = |
| reinterpret_cast<const vm_address_t>(bytes.data()); |
| |
| auto memory = |
| std::make_unique<internal::MemorySnapshotIOSIntermediateDump>(); |
| memory->Initialize(address, data, data_size); |
| extra_memory_.push_back(std::move(memory)); |
| } |
| } |
| } |
| |
| INITIALIZATION_STATE_SET_VALID(initialized_); |
| return true; |
| } |
| const CPUContext* ThreadSnapshotIOSIntermediateDump::Context() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return &context_; |
| } |
| |
| const MemorySnapshot* ThreadSnapshotIOSIntermediateDump::Stack() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return &stack_; |
| } |
| |
| uint64_t ThreadSnapshotIOSIntermediateDump::ThreadID() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return thread_id_; |
| } |
| |
| std::string ThreadSnapshotIOSIntermediateDump::ThreadName() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return thread_name_; |
| } |
| |
| int ThreadSnapshotIOSIntermediateDump::SuspendCount() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return suspend_count_; |
| } |
| |
| int ThreadSnapshotIOSIntermediateDump::Priority() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return priority_; |
| } |
| |
| uint64_t ThreadSnapshotIOSIntermediateDump::ThreadSpecificDataAddress() const { |
| INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| return thread_specific_data_address_; |
| } |
| |
| std::vector<const MemorySnapshot*> |
| ThreadSnapshotIOSIntermediateDump::ExtraMemory() const { |
| std::vector<const MemorySnapshot*> extra_memory; |
| for (const auto& memory : extra_memory_) { |
| extra_memory.push_back(memory.get()); |
| } |
| return extra_memory; |
| } |
| |
| } // namespace internal |
| } // namespace crashpad |