blob: dd9567fc946d31b0f77fc3608969ce0c6f92f56c [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/profiler/native_unwinder_ios.h"
#include <pthread/stack_np.h>
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/profiler/module_cache.h"
#include "base/profiler/native_unwinder.h"
#include "build/build_config.h"
namespace base {
NativeUnwinderIOS::NativeUnwinderIOS() {}
bool NativeUnwinderIOS::CanUnwindFrom(const Frame& current_frame) const {
return current_frame.module && current_frame.module->IsNative();
}
UnwindResult NativeUnwinderIOS::TryUnwind(RegisterContext* thread_context,
uintptr_t stack_top,
std::vector<Frame>* stack) const {
// We expect the frame corresponding to the |thread_context| register state to
// exist within |stack|.
DCHECK_GT(stack->size(), 0u);
#if defined(ARCH_CPU_ARM64)
const uintptr_t align_mask = 0x1;
const uintptr_t stack_bottom = thread_context->__sp;
uintptr_t next_frame = thread_context->__fp;
#elif defined(ARCH_CPU_X86_64)
const uintptr_t align_mask = 0xf;
const uintptr_t stack_bottom = thread_context->__rsp;
uintptr_t next_frame = thread_context->__rbp;
#endif
const auto is_fp_valid = [&](uintptr_t fp) {
return fp >= stack_bottom && fp < stack_top && (fp & align_mask) == 0;
};
if (!is_fp_valid(next_frame)) {
return UnwindResult::ABORTED;
}
for (;;) {
uintptr_t retaddr;
uintptr_t frame = next_frame;
next_frame = pthread_stack_frame_decode_np(frame, &retaddr);
if (!is_fp_valid(frame) || next_frame <= frame) {
return UnwindResult::COMPLETED;
}
stack->emplace_back(retaddr, module_cache()->GetModuleForAddress(retaddr));
}
NOTREACHED();
return UnwindResult::COMPLETED;
}
std::unique_ptr<Unwinder> CreateNativeUnwinder(ModuleCache* module_cache) {
return std::make_unique<NativeUnwinderIOS>();
}
} // namespace base