blob: d37ccef3bf8a1aa00a17728f98440fa316820761 [file] [log] [blame]
// 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