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