1 // Copyright 2021 the V8 project 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 5 #ifndef INCLUDE_V8_UNWINDER_H_ 6 #define INCLUDE_V8_UNWINDER_H_ 7 8 #include <memory> 9 10 #include "v8config.h" // NOLINT(build/include_directory) 11 12 namespace v8 { 13 // Holds the callee saved registers needed for the stack unwinder. It is the 14 // empty struct if no registers are required. Implemented in 15 // include/v8-unwinder-state.h. 16 struct CalleeSavedRegisters; 17 18 // A RegisterState represents the current state of registers used 19 // by the sampling profiler API. 20 struct V8_EXPORT RegisterState { 21 RegisterState(); 22 ~RegisterState(); 23 RegisterState(const RegisterState& other); 24 RegisterState& operator=(const RegisterState& other); 25 26 void* pc; // Instruction pointer. 27 void* sp; // Stack pointer. 28 void* fp; // Frame pointer. 29 void* lr; // Link register (or nullptr on platforms without a link register). 30 // Callee saved registers (or null if no callee saved registers were stored) 31 std::unique_ptr<CalleeSavedRegisters> callee_saved; 32 }; 33 34 // A StateTag represents a possible state of the VM. 35 enum StateTag { 36 JS, 37 GC, 38 PARSER, 39 BYTECODE_COMPILER, 40 COMPILER, 41 OTHER, 42 EXTERNAL, 43 ATOMICS_WAIT, 44 IDLE 45 }; 46 47 // The output structure filled up by GetStackSample API function. 48 struct SampleInfo { 49 size_t frames_count; // Number of frames collected. 50 StateTag vm_state; // Current VM state. 51 void* external_callback_entry; // External callback address if VM is 52 // executing an external callback. 53 void* context; // Incumbent native context address. 54 }; 55 56 struct MemoryRange { 57 const void* start = nullptr; 58 size_t length_in_bytes = 0; 59 }; 60 61 struct JSEntryStub { 62 MemoryRange code; 63 }; 64 65 struct JSEntryStubs { 66 JSEntryStub js_entry_stub; 67 JSEntryStub js_construct_entry_stub; 68 JSEntryStub js_run_microtasks_entry_stub; 69 }; 70 71 /** 72 * Various helpers for skipping over V8 frames in a given stack. 73 * 74 * The unwinder API is only supported on the x64, ARM64 and ARM32 architectures. 75 */ 76 class V8_EXPORT Unwinder { 77 public: 78 /** 79 * Attempt to unwind the stack to the most recent C++ frame. This function is 80 * signal-safe and does not access any V8 state and thus doesn't require an 81 * Isolate. 82 * 83 * The unwinder needs to know the location of the JS Entry Stub (a piece of 84 * code that is run when C++ code calls into generated JS code). This is used 85 * for edge cases where the current frame is being constructed or torn down 86 * when the stack sample occurs. 87 * 88 * The unwinder also needs the virtual memory range of all possible V8 code 89 * objects. There are two ranges required - the heap code range and the range 90 * for code embedded in the binary. 91 * 92 * Available on x64, ARM64 and ARM32. 93 * 94 * \param code_pages A list of all of the ranges in which V8 has allocated 95 * executable code. The caller should obtain this list by calling 96 * Isolate::CopyCodePages() during the same interrupt/thread suspension that 97 * captures the stack. 98 * \param register_state The current registers. This is an in-out param that 99 * will be overwritten with the register values after unwinding, on success. 100 * \param stack_base The resulting stack pointer and frame pointer values are 101 * bounds-checked against the stack_base and the original stack pointer value 102 * to ensure that they are valid locations in the given stack. If these values 103 * or any intermediate frame pointer values used during unwinding are ever out 104 * of these bounds, unwinding will fail. 105 * 106 * \return True on success. 107 */ 108 static bool TryUnwindV8Frames(const JSEntryStubs& entry_stubs, 109 size_t code_pages_length, 110 const MemoryRange* code_pages, 111 RegisterState* register_state, 112 const void* stack_base); 113 114 /** 115 * Whether the PC is within the V8 code range represented by code_pages. 116 * 117 * If this returns false, then calling UnwindV8Frames() with the same PC 118 * and unwind_state will always fail. If it returns true, then unwinding may 119 * (but not necessarily) be successful. 120 * 121 * Available on x64, ARM64 and ARM32 122 */ 123 static bool PCIsInV8(size_t code_pages_length, const MemoryRange* code_pages, 124 void* pc); 125 }; 126 127 } // namespace v8 128 129 #endif // INCLUDE_V8_UNWINDER_H_ 130