frame_pointer_unwinder.cc 4.0 KB

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