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