Fix emulator build error
[platform/framework/web/chromium-efl.git] / base / profiler / frame_pointer_unwinder.cc
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/profiler/frame_pointer_unwinder.h"
6
7 #include "base/check_op.h"
8 #include "base/notreached.h"
9 #include "base/numerics/clamped_math.h"
10 #include "base/profiler/module_cache.h"
11 #include "build/build_config.h"
12
13 #if BUILDFLAG(IS_APPLE)
14 #include <pthread/stack_np.h>
15 #endif
16
17 namespace {
18
19 // Given a frame pointer, returns the frame pointer of the calling stack
20 // frame and places the return address of the calling stack frame into
21 // `return_address`. Shim around `pthread_stack_frame_decode_np` where
22 // available since it handles pointer authentication on supported platforms.
23 // NB: The caller *must* ensure that there are 2+ uintptr_t's worth of memory at
24 // `frame_pointer`.
25 uintptr_t DecodeFrame(uintptr_t frame_pointer, uintptr_t* return_address) {
26 #if BUILDFLAG(IS_APPLE)
27   if (__builtin_available(iOS 12, *)) {
28     return pthread_stack_frame_decode_np(frame_pointer, return_address);
29   }
30 #endif
31   const uintptr_t* fp = reinterpret_cast<uintptr_t*>(frame_pointer);
32   uintptr_t next_frame = *fp;
33   *return_address = *(fp + 1);
34   return next_frame;
35 }
36
37 }  // namespace
38
39 namespace base {
40
41 FramePointerUnwinder::FramePointerUnwinder() = default;
42
43 bool FramePointerUnwinder::CanUnwindFrom(const Frame& current_frame) const {
44   return current_frame.module && current_frame.module->IsNative();
45 }
46
47 UnwindResult FramePointerUnwinder::TryUnwind(RegisterContext* thread_context,
48                                              uintptr_t stack_top,
49                                              std::vector<Frame>* stack) {
50   // We expect the frame corresponding to the |thread_context| register state to
51   // exist within |stack|.
52   DCHECK_GT(stack->size(), 0u);
53 #if defined(ARCH_CPU_ARM64)
54   constexpr uintptr_t align_mask = 0x1;
55 #elif defined(ARCH_CPU_X86_64)
56   constexpr uintptr_t align_mask = 0xf;
57 #endif
58
59   uintptr_t next_frame = RegisterContextFramePointer(thread_context);
60   uintptr_t frame_lower_bound = RegisterContextStackPointer(thread_context);
61   const auto is_fp_valid = [&](uintptr_t fp) {
62     // Ensure there's space on the stack to read two values: the caller's
63     // frame pointer and the return address.
64     return next_frame >= frame_lower_bound &&
65            ClampAdd(next_frame, sizeof(uintptr_t) * 2) <= stack_top &&
66            (next_frame & align_mask) == 0;
67   };
68   if (!is_fp_valid(next_frame))
69     return UnwindResult::kAborted;
70
71   for (;;) {
72     if (!stack->back().module) {
73       return UnwindResult::kAborted;
74     }
75     if (!stack->back().module->IsNative()) {
76       // This is a non-native module associated with the auxiliary unwinder
77       // (e.g. corresponding to a frame in V8 generated code). Report as
78       // UNRECOGNIZED_FRAME to allow that unwinder to unwind the frame.
79       return UnwindResult::kUnrecognizedFrame;
80     }
81     uintptr_t retaddr;
82     uintptr_t frame = next_frame;
83     next_frame = DecodeFrame(frame, &retaddr);
84     frame_lower_bound = frame + 1;
85     // If `next_frame` is 0, we've hit the root and `retaddr` isn't useful.
86     // Bail without recording the frame.
87     if (next_frame == 0)
88       return UnwindResult::kCompleted;
89     const ModuleCache::Module* module =
90         module_cache()->GetModuleForAddress(retaddr);
91     // V8 doesn't conform to the x86_64 ABI re: stack alignment. For V8 frames,
92     // let the V8 unwinder determine whether the FP is valid or not.
93     bool is_non_native_module = module && !module->IsNative();
94     // If the FP doesn't look correct, don't record this frame.
95     if (!is_non_native_module && !is_fp_valid(next_frame))
96       return UnwindResult::kAborted;
97
98     RegisterContextFramePointer(thread_context) = next_frame;
99     RegisterContextInstructionPointer(thread_context) = retaddr;
100     RegisterContextStackPointer(thread_context) = frame + sizeof(uintptr_t) * 2;
101     stack->emplace_back(retaddr, module);
102   }
103
104   NOTREACHED();
105   return UnwindResult::kCompleted;
106 }
107
108 }  // namespace base