v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
stack.h
Go to the documentation of this file.
1// Copyright 2020 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 V8_HEAP_BASE_STACK_H_
6#define V8_HEAP_BASE_STACK_H_
7
8#include <map>
9#include <vector>
10
11#include "src/base/macros.h"
14
15namespace heap::base {
16
18 public:
19 virtual ~StackVisitor() = default;
20 virtual void VisitPointer(const void* address) = 0;
21};
22
24
25// Abstraction over the stack. Supports handling of:
26// - native stack;
27// - ASAN/MSAN;
28// - SafeStack: https://releases.llvm.org/10.0.0/tools/clang/docs/SafeStack.html
29//
30// Stacks grow down, so throughout this class "start" refers the highest
31// address of the stack, and top/marker the lowest.
32//
33// TODO(chromium:1056170): Consider adding a component that keeps track
34// of relevant GC stack regions where interesting pointers can be found.
36 public:
37 // Sets the start of the stack to the current stack start.
39 current_segment_.start = v8::base::Stack::GetStackStart();
40#ifdef V8_USE_ADDRESS_SANITIZER
41 current_segment_.asan_fake_stack = __asan_get_current_fake_stack();
42#endif // V8_USE_ADDRESS_SANITIZER
43#ifdef V8_USE_SAFE_STACK
44 current_segment_.unsafe_stack_start = __builtin___get_unsafe_stack_top();
45#endif // V8_USE_SAFE_STACK
46 }
47
48 // Returns true if |slot| is part of the stack and false otherwise.
49 // It is a static method, ignoring the set stack start and marker, but it
50 // considers the ASAN stack and SafeStack.
51 static bool IsOnStack(const void* slot);
52
53 void IteratePointers(StackVisitor* visitor) const {
54 IteratePointersUntilMarker(visitor);
55 IterateBackgroundStacks(visitor);
56 }
57
58 // Word-aligned iteration of the stack, starting at the `stack_marker_`
59 // and going to the stack start. Slot values are passed on to `visitor`.
60 void IteratePointersUntilMarker(StackVisitor* visitor) const;
61
62 // Iterate just the background stacks, if any.
63 void IterateBackgroundStacks(StackVisitor* visitor) const;
64
65 // Push callee-saved registers to the stack, set the stack marker to the
66 // current stack top and invoke the callback.
67 template <typename Callback>
69 TrampolineCallbackHelper(static_cast<void*>(&callback),
70 &SetMarkerAndCallbackImpl<Callback>);
71 }
72
73 template <typename Callback>
75 if (!IsMarkerSet()) {
76 TrampolineCallbackHelper(static_cast<void*>(&callback),
77 &SetMarkerAndCallbackImpl<Callback>);
78 } else {
79 DCHECK(IsOnCurrentStack(current_segment_.top));
80 callback();
81 }
82 }
83
84 using ThreadId = int;
85
86 template <typename Callback>
88 Callback callback) {
89 std::pair<ThreadId, Callback*> info{thread, &callback};
90 TrampolineCallbackHelper(
91 static_cast<void*>(&info),
92 &SetMarkerForBackgroundThreadAndCallbackImpl<Callback>);
93 }
94
95 using IterateStackCallback = void (*)(Stack*, void*, const void*);
96
97 // This method combines SetMarkerAndCallback with IteratePointers.
98 // Callee-saved registers are pushed to the stack and then a word-aligned
99 // iteration of the stack is performed. Slot values are passed on to
100 // `visitor`. To be used for testing.
101 void IteratePointersForTesting(StackVisitor* visitor);
102
103 bool IsMarkerSet() const { return current_segment_.top != nullptr; }
105 v8::base::MutexGuard guard(&lock_);
106 auto it = background_stacks_.find(thread);
107 if (it == background_stacks_.end()) return false;
108 DCHECK_NOT_NULL(it->second.top);
109 return true;
110 }
111
112 // This method is only safe to use in a safepoint, as it does not take the
113 // mutex for background_stacks_.
114 bool HasBackgroundStacks() const { return !background_stacks_.empty(); }
115
117 scan_simulator_callback_ = callback;
118 }
119
120 // Stack segments that may contain pointers and should be scanned.
121 struct Segment {
122 // The start and top of the stack. It must be sp <= top <= start.
123 // The top pointer is generally a marker that signals the end of the
124 // interesting stack region in which on-heap pointers can be found.
125 const void* start = nullptr;
126 const void* top = nullptr;
127
128#ifdef V8_USE_ADDRESS_SANITIZER
129 // The start of ASAN's fake stack.
130 const void* asan_fake_stack = nullptr;
131#endif // V8_USE_ADDRESS_SANITIZER
132
133#ifdef V8_USE_SAFE_STACK
134 // Start and top for the unsafe stack that is used in clang with
135 // -fsanitizer=safe-stack.
136 // It must be unsafe_sp <= unsafe_stack_top <= unsafe_stack_start.
137 // Notice that the terms "start" and "top" have here a different meaning in
138 // the terminology used in this feature's documentation.
139 const void* unsafe_stack_start = nullptr;
140 const void* unsafe_stack_top = nullptr;
141#endif // V8_USE_SAFE_STACK
142
143 Segment() = default;
144 Segment(const void* stack_start, const void* stack_top)
145 : start(stack_start), top(stack_top) {
146#ifdef V8_USE_ADDRESS_SANITIZER
147 asan_fake_stack = __asan_get_current_fake_stack();
148#endif // V8_USE_ADDRESS_SANITIZER
149#ifdef V8_USE_SAFE_STACK
150 unsafe_stack_start = __builtin___get_unsafe_stack_top();
151 unsafe_stack_top = __builtin___get_unsafe_stack_ptr();
152#endif // V8_USE_SAFE_STACK
153 }
154 };
155
156 private:
157#ifdef DEBUG
158 static bool IsOnCurrentStack(const void* ptr);
159#endif
160
161 V8_NOINLINE void TrampolineCallbackHelper(void* argument,
163
164 template <typename Callback>
165 static void SetMarkerAndCallbackImpl(Stack* stack, void* argument,
166 const void* stack_end) {
167 Segment previous_segment = stack->current_segment_;
168 stack->current_segment_.top = stack_end;
169#ifdef V8_USE_SAFE_STACK
170 stack->current_segment_.unsafe_stack_top =
171 __builtin___get_unsafe_stack_ptr();
172#endif // V8_USE_SAFE_STACK
173 Callback* callback = static_cast<Callback*>(argument);
174 (*callback)();
175 stack->current_segment_ = previous_segment;
176 }
177
178 template <typename Callback>
180 Stack* stack, void* argument, const void* stack_end) {
181 DCHECK(IsOnCurrentStack(stack_end));
182 auto [thread, callback] =
183 *static_cast<std::pair<ThreadId, Callback*>*>(argument);
184 auto& background_stacks = stack->background_stacks_;
185 Segment previous_segment;
186 {
187 v8::base::MutexGuard guard(&stack->lock_);
188 if (auto it = background_stacks.find(thread);
189 it != background_stacks.end()) {
190 previous_segment = it->second;
191 DCHECK_NOT_NULL(previous_segment.top);
192 } else {
193 DCHECK_NULL(previous_segment.top);
194 }
195 // This implicitly uses the current values (if applicable) for:
196 // - asan_fake_start
197 // - unsafe stack start
198 // - unsafe stack top
199 background_stacks[thread] =
201 }
202 (*callback)();
203 {
204 v8::base::MutexGuard guard(&stack->lock_);
205 if (previous_segment.top)
206 background_stacks[thread] = previous_segment;
207 else
208 background_stacks.erase(thread);
209 }
210 }
211
213
215 std::map<ThreadId, Segment> background_stacks_;
216
217 StackVisitorCallback scan_simulator_callback_ = nullptr;
218};
219
220} // namespace heap::base
221
222#endif // V8_HEAP_BASE_STACK_H_
virtual void VisitPointer(const void *address)=0
virtual ~StackVisitor()=default
v8::base::Mutex lock_
Definition stack.h:214
Segment current_segment_
Definition stack.h:212
void(*)(Stack *, void *, const void *) IterateStackCallback
Definition stack.h:95
bool IsMarkerSet() const
Definition stack.h:103
void SetStackStart()
Definition stack.h:38
void IteratePointers(StackVisitor *visitor) const
Definition stack.h:53
V8_INLINE void SetMarkerIfNeededAndCallback(Callback callback)
Definition stack.h:74
static void SetMarkerForBackgroundThreadAndCallbackImpl(Stack *stack, void *argument, const void *stack_end)
Definition stack.h:179
static void SetMarkerAndCallbackImpl(Stack *stack, void *argument, const void *stack_end)
Definition stack.h:165
V8_INLINE void SetMarkerForBackgroundThreadAndCallback(ThreadId thread, Callback callback)
Definition stack.h:87
void SetScanSimulatorCallback(StackVisitorCallback callback)
Definition stack.h:116
bool HasBackgroundStacks() const
Definition stack.h:114
V8_INLINE void SetMarkerAndCallback(Callback callback)
Definition stack.h:68
std::map< ThreadId, Segment > background_stacks_
Definition stack.h:215
bool IsMarkerSetForBackgroundThread(ThreadId thread) const
Definition stack.h:104
static StackSlot GetStackStart()
int start
TNode< Object > callback
void(*)(const Stack *, StackVisitor *, intptr_t *) IterateStackCallback
void(*)(StackVisitor *) StackVisitorCallback
Definition stack.h:23
#define DCHECK_NULL(val)
Definition logging.h:491
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460
Segment(const void *stack_start, const void *stack_top)
Definition stack.h:144
#define V8_INLINE
Definition v8config.h:500
#define V8_NOINLINE
Definition v8config.h:586