Line data Source code
1 : // Copyright 2018 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 : #include "include/v8.h"
6 :
7 : #include "src/api-inl.h"
8 : #include "src/builtins/builtins.h"
9 : #include "src/heap/spaces.h"
10 : #include "src/isolate.h"
11 : #include "src/objects/code-inl.h"
12 : #include "test/cctest/cctest.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 : namespace test_unwinder {
17 :
18 : static void* unlimited_stack_base = std::numeric_limits<void*>::max();
19 :
20 26644 : TEST(Unwind_BadState_Fail) {
21 5 : UnwindState unwind_state; // Fields are intialized to nullptr.
22 : RegisterState register_state;
23 :
24 5 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
25 5 : unlimited_stack_base);
26 5 : CHECK(!unwound);
27 : // The register state should not change when unwinding fails.
28 5 : CHECK_NULL(register_state.fp);
29 5 : CHECK_NULL(register_state.sp);
30 5 : CHECK_NULL(register_state.pc);
31 5 : }
32 :
33 26644 : TEST(Unwind_BuiltinPCInMiddle_Success) {
34 5 : LocalContext env;
35 5 : v8::Isolate* isolate = env->GetIsolate();
36 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
37 :
38 5 : UnwindState unwind_state = isolate->GetUnwindState();
39 : RegisterState register_state;
40 :
41 : uintptr_t stack[3];
42 : void* stack_base = stack + arraysize(stack);
43 5 : stack[0] = reinterpret_cast<uintptr_t>(stack + 2); // saved FP (rbp).
44 5 : stack[1] = 202; // Return address into C++ code.
45 5 : stack[2] = 303; // The SP points here in the caller's frame.
46 :
47 5 : register_state.sp = stack;
48 5 : register_state.fp = stack;
49 :
50 : // Put the current PC inside of a valid builtin.
51 5 : Code builtin = i_isolate->builtins()->builtin(Builtins::kStringEqual);
52 : const uintptr_t offset = 40;
53 5 : CHECK_LT(offset, builtin->InstructionSize());
54 : register_state.pc =
55 5 : reinterpret_cast<void*>(builtin->InstructionStart() + offset);
56 :
57 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
58 5 : stack_base);
59 5 : CHECK(unwound);
60 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.fp);
61 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.sp);
62 5 : CHECK_EQ(reinterpret_cast<void*>(202), register_state.pc);
63 5 : }
64 :
65 : // The unwinder should be able to unwind even if we haven't properly set up the
66 : // current frame, as long as there is another JS frame underneath us (i.e. as
67 : // long as the PC isn't in JSEntry). This test puts the PC at the start
68 : // of a JS builtin and creates a fake JSEntry frame before it on the stack. The
69 : // unwinder should be able to unwind to the C++ frame before the JSEntry frame.
70 26644 : TEST(Unwind_BuiltinPCAtStart_Success) {
71 5 : LocalContext env;
72 5 : v8::Isolate* isolate = env->GetIsolate();
73 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
74 :
75 5 : UnwindState unwind_state = isolate->GetUnwindState();
76 : RegisterState register_state;
77 :
78 : const size_t code_length = 40;
79 5 : uintptr_t code[code_length] = {0};
80 5 : unwind_state.code_range.start = code;
81 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
82 :
83 : uintptr_t stack[6];
84 : void* stack_base = stack + arraysize(stack);
85 5 : stack[0] = 101;
86 : // Return address into JS code. It doesn't matter that this is not actually in
87 : // JSEntry, because we only check that for the top frame.
88 5 : stack[1] = reinterpret_cast<uintptr_t>(code + 10);
89 5 : stack[2] = reinterpret_cast<uintptr_t>(stack + 5); // saved FP (rbp).
90 5 : stack[3] = 303; // Return address into C++ code.
91 5 : stack[4] = 404;
92 5 : stack[5] = 505;
93 :
94 5 : register_state.sp = stack;
95 5 : register_state.fp = stack + 2; // FP to the JSEntry frame.
96 :
97 : // Put the current PC at the start of a valid builtin, so that we are setting
98 : // up the frame.
99 5 : Code builtin = i_isolate->builtins()->builtin(Builtins::kStringEqual);
100 5 : register_state.pc = reinterpret_cast<void*>(builtin->InstructionStart());
101 :
102 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
103 5 : stack_base);
104 :
105 5 : CHECK(unwound);
106 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 5), register_state.fp);
107 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 4), register_state.sp);
108 5 : CHECK_EQ(reinterpret_cast<void*>(303), register_state.pc);
109 5 : }
110 :
111 : const char* foo_source = R"(
112 : function foo(a, b) {
113 : let x = a * b;
114 : let y = x ^ b;
115 : let z = y / a;
116 : return x + y - z;
117 : }
118 : foo(1, 2);
119 : foo(1, 2);
120 : %OptimizeFunctionOnNextCall(foo);
121 : foo(1, 2);
122 : )";
123 :
124 : // Check that we can unwind when the pc is within an optimized code object on
125 : // the V8 heap.
126 26644 : TEST(Unwind_CodeObjectPCInMiddle_Success) {
127 5 : FLAG_allow_natives_syntax = true;
128 4 : LocalContext env;
129 5 : v8::Isolate* isolate = env->GetIsolate();
130 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
131 : HandleScope scope(i_isolate);
132 :
133 5 : UnwindState unwind_state = isolate->GetUnwindState();
134 : RegisterState register_state;
135 :
136 : uintptr_t stack[3];
137 : void* stack_base = stack + arraysize(stack);
138 5 : stack[0] = reinterpret_cast<uintptr_t>(stack + 2); // saved FP (rbp).
139 5 : stack[1] = 202; // Return address into C++ code.
140 5 : stack[2] = 303; // The SP points here in the caller's frame.
141 :
142 5 : register_state.sp = stack;
143 5 : register_state.fp = stack;
144 :
145 : // Create an on-heap code object. Make sure we run the function so that it is
146 : // compiled and not just marked for lazy compilation.
147 5 : CompileRun(foo_source);
148 : v8::Local<v8::Function> local_foo = v8::Local<v8::Function>::Cast(
149 20 : env.local()->Global()->Get(env.local(), v8_str("foo")).ToLocalChecked());
150 : Handle<JSFunction> foo =
151 : Handle<JSFunction>::cast(v8::Utils::OpenHandle(*local_foo));
152 :
153 : // Put the current PC inside of the created code object.
154 5 : AbstractCode abstract_code = foo->abstract_code();
155 : // We don't produce optimized code when run with --no-opt.
156 5 : if (!abstract_code->IsCode() && FLAG_opt == false) return;
157 4 : CHECK(abstract_code->IsCode());
158 :
159 4 : Code code = abstract_code->GetCode();
160 : // We don't want the offset too early or it could be the `push rbp`
161 : // instruction (which is not at the start of generated code, because the lazy
162 : // deopt check happens before frame setup).
163 4 : const uintptr_t offset = code->InstructionSize() - 20;
164 4 : CHECK_LT(offset, code->InstructionSize());
165 4 : Address pc = code->InstructionStart() + offset;
166 4 : register_state.pc = reinterpret_cast<void*>(pc);
167 :
168 : // Check that the created code is within the code range that we get from the
169 : // API.
170 4 : Address start = reinterpret_cast<Address>(unwind_state.code_range.start);
171 4 : CHECK(pc >= start && pc < start + unwind_state.code_range.length_in_bytes);
172 :
173 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
174 4 : stack_base);
175 4 : CHECK(unwound);
176 4 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.fp);
177 4 : CHECK_EQ(reinterpret_cast<void*>(stack + 2), register_state.sp);
178 4 : CHECK_EQ(reinterpret_cast<void*>(202), register_state.pc);
179 : }
180 :
181 : // If the PC is within JSEntry but we haven't set up the frame yet, then we
182 : // cannot unwind.
183 26644 : TEST(Unwind_JSEntryBeforeFrame_Fail) {
184 5 : LocalContext env;
185 5 : v8::Isolate* isolate = env->GetIsolate();
186 :
187 5 : UnwindState unwind_state = isolate->GetUnwindState();
188 : RegisterState register_state;
189 :
190 : const size_t code_length = 40;
191 5 : uintptr_t code[code_length] = {0};
192 5 : unwind_state.code_range.start = code;
193 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
194 :
195 : // Pretend that it takes 5 instructions to set up the frame in JSEntry.
196 5 : unwind_state.js_entry_stub.code.start = code + 10;
197 5 : unwind_state.js_entry_stub.code.length_in_bytes = 10 * sizeof(uintptr_t);
198 :
199 : uintptr_t stack[10];
200 : void* stack_base = stack + arraysize(stack);
201 5 : stack[0] = 101;
202 5 : stack[1] = 111;
203 5 : stack[2] = 121;
204 5 : stack[3] = 131;
205 5 : stack[4] = 141;
206 5 : stack[5] = 151;
207 5 : stack[6] = 100; // Return address into C++ code.
208 5 : stack[7] = 303; // The SP points here in the caller's frame.
209 5 : stack[8] = 404;
210 5 : stack[9] = 505;
211 :
212 5 : register_state.sp = stack + 5;
213 5 : register_state.fp = stack + 9;
214 :
215 : // Put the current PC inside of JSEntry, before the frame is set up.
216 5 : register_state.pc = code + 12;
217 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
218 5 : stack_base);
219 5 : CHECK(!unwound);
220 : // The register state should not change when unwinding fails.
221 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
222 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 5), register_state.sp);
223 5 : CHECK_EQ(code + 12, register_state.pc);
224 :
225 : // Change the PC to a few instructions later, after the frame is set up.
226 5 : register_state.pc = code + 16;
227 : unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
228 5 : stack_base);
229 : // TODO(petermarshall): More precisely check position within JSEntry rather
230 : // than just assuming the frame is unreadable.
231 5 : CHECK(!unwound);
232 : // The register state should not change when unwinding fails.
233 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
234 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 5), register_state.sp);
235 5 : CHECK_EQ(code + 16, register_state.pc);
236 5 : }
237 :
238 26644 : TEST(Unwind_OneJSFrame_Success) {
239 5 : LocalContext env;
240 5 : v8::Isolate* isolate = env->GetIsolate();
241 :
242 5 : UnwindState unwind_state = isolate->GetUnwindState();
243 : RegisterState register_state;
244 :
245 : // Use a fake code range so that we can initialize it to 0s.
246 : const size_t code_length = 40;
247 5 : uintptr_t code[code_length] = {0};
248 5 : unwind_state.code_range.start = code;
249 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
250 :
251 : // Our fake stack has two frames - one C++ frame and one JS frame (on top).
252 : // The stack grows from high addresses to low addresses.
253 : uintptr_t stack[10];
254 : void* stack_base = stack + arraysize(stack);
255 5 : stack[0] = 101;
256 5 : stack[1] = 111;
257 5 : stack[2] = 121;
258 5 : stack[3] = 131;
259 5 : stack[4] = 141;
260 5 : stack[5] = reinterpret_cast<uintptr_t>(stack + 9); // saved FP (rbp).
261 5 : stack[6] = 100; // Return address into C++ code.
262 5 : stack[7] = 303; // The SP points here in the caller's frame.
263 5 : stack[8] = 404;
264 5 : stack[9] = 505;
265 :
266 5 : register_state.sp = stack;
267 5 : register_state.fp = stack + 5;
268 :
269 : // Put the current PC inside of the code range so it looks valid.
270 5 : register_state.pc = code + 30;
271 :
272 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
273 5 : stack_base);
274 :
275 5 : CHECK(unwound);
276 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
277 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 7), register_state.sp);
278 5 : CHECK_EQ(reinterpret_cast<void*>(100), register_state.pc);
279 5 : }
280 :
281 : // Creates a fake stack with two JS frames on top of a C++ frame and checks that
282 : // the unwinder correctly unwinds past the JS frames and returns the C++ frame's
283 : // details.
284 26644 : TEST(Unwind_TwoJSFrames_Success) {
285 5 : LocalContext env;
286 5 : v8::Isolate* isolate = env->GetIsolate();
287 :
288 5 : UnwindState unwind_state = isolate->GetUnwindState();
289 : RegisterState register_state;
290 :
291 : // Use a fake code range so that we can initialize it to 0s.
292 : const size_t code_length = 40;
293 5 : uintptr_t code[code_length] = {0};
294 5 : unwind_state.code_range.start = code;
295 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
296 :
297 : // Our fake stack has three frames - one C++ frame and two JS frames (on top).
298 : // The stack grows from high addresses to low addresses.
299 : uintptr_t stack[10];
300 : void* stack_base = stack + arraysize(stack);
301 5 : stack[0] = 101;
302 5 : stack[1] = 111;
303 5 : stack[2] = reinterpret_cast<uintptr_t>(stack + 5); // saved FP (rbp).
304 : // The fake return address is in the JS code range.
305 5 : stack[3] = reinterpret_cast<uintptr_t>(code + 10);
306 5 : stack[4] = 141;
307 5 : stack[5] = reinterpret_cast<uintptr_t>(stack + 9); // saved FP (rbp).
308 5 : stack[6] = 100; // Return address into C++ code.
309 5 : stack[7] = 303; // The SP points here in the caller's frame.
310 5 : stack[8] = 404;
311 5 : stack[9] = 505;
312 :
313 5 : register_state.sp = stack;
314 5 : register_state.fp = stack + 2;
315 :
316 : // Put the current PC inside of the code range so it looks valid.
317 5 : register_state.pc = code + 30;
318 :
319 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
320 5 : stack_base);
321 :
322 5 : CHECK(unwound);
323 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 9), register_state.fp);
324 5 : CHECK_EQ(reinterpret_cast<void*>(stack + 7), register_state.sp);
325 5 : CHECK_EQ(reinterpret_cast<void*>(100), register_state.pc);
326 5 : }
327 :
328 : // If the PC is in JSEntry then the frame might not be set up correctly, meaning
329 : // we can't unwind the stack properly.
330 26644 : TEST(Unwind_JSEntry_Fail) {
331 5 : LocalContext env;
332 5 : v8::Isolate* isolate = env->GetIsolate();
333 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
334 :
335 5 : UnwindState unwind_state = isolate->GetUnwindState();
336 : RegisterState register_state;
337 :
338 5 : Code js_entry = i_isolate->heap()->builtin(Builtins::kJSEntry);
339 5 : byte* start = reinterpret_cast<byte*>(js_entry->InstructionStart());
340 5 : register_state.pc = start + 10;
341 :
342 5 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
343 5 : unlimited_stack_base);
344 5 : CHECK(!unwound);
345 : // The register state should not change when unwinding fails.
346 5 : CHECK_NULL(register_state.fp);
347 5 : CHECK_NULL(register_state.sp);
348 5 : CHECK_EQ(start + 10, register_state.pc);
349 5 : }
350 :
351 26644 : TEST(Unwind_StackBounds_Basic) {
352 5 : LocalContext env;
353 5 : v8::Isolate* isolate = env->GetIsolate();
354 :
355 5 : UnwindState unwind_state = isolate->GetUnwindState();
356 : RegisterState register_state;
357 :
358 : const size_t code_length = 10;
359 5 : uintptr_t code[code_length] = {0};
360 5 : unwind_state.code_range.start = code;
361 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
362 :
363 : uintptr_t stack[3];
364 5 : stack[0] = reinterpret_cast<uintptr_t>(stack + 2); // saved FP (rbp).
365 5 : stack[1] = 202; // Return address into C++ code.
366 5 : stack[2] = 303; // The SP points here in the caller's frame.
367 :
368 5 : register_state.sp = stack;
369 5 : register_state.fp = stack;
370 5 : register_state.pc = code;
371 :
372 : void* wrong_stack_base = reinterpret_cast<void*>(
373 5 : reinterpret_cast<uintptr_t>(stack) - sizeof(uintptr_t));
374 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
375 5 : wrong_stack_base);
376 5 : CHECK(!unwound);
377 :
378 : // Correct the stack base and unwinding should succeed.
379 : void* correct_stack_base = stack + arraysize(stack);
380 : unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
381 5 : correct_stack_base);
382 5 : CHECK(unwound);
383 5 : }
384 :
385 26644 : TEST(Unwind_StackBounds_WithUnwinding) {
386 5 : LocalContext env;
387 5 : v8::Isolate* isolate = env->GetIsolate();
388 :
389 5 : UnwindState unwind_state = isolate->GetUnwindState();
390 : RegisterState register_state;
391 :
392 : // Use a fake code range so that we can initialize it to 0s.
393 : const size_t code_length = 40;
394 5 : uintptr_t code[code_length] = {0};
395 5 : unwind_state.code_range.start = code;
396 5 : unwind_state.code_range.length_in_bytes = code_length * sizeof(uintptr_t);
397 :
398 : // Our fake stack has two frames - one C++ frame and one JS frame (on top).
399 : // The stack grows from high addresses to low addresses.
400 : uintptr_t stack[11];
401 : void* stack_base = stack + arraysize(stack);
402 5 : stack[0] = 101;
403 5 : stack[1] = 111;
404 5 : stack[2] = 121;
405 5 : stack[3] = 131;
406 5 : stack[4] = 141;
407 5 : stack[5] = reinterpret_cast<uintptr_t>(stack + 9); // saved FP (rbp).
408 5 : stack[6] = reinterpret_cast<uintptr_t>(code + 20); // JS code.
409 5 : stack[7] = 303; // The SP points here in the caller's frame.
410 5 : stack[8] = 404;
411 5 : stack[9] = reinterpret_cast<uintptr_t>(stack) +
412 5 : (12 * sizeof(uintptr_t)); // saved FP (OOB).
413 5 : stack[10] = reinterpret_cast<uintptr_t>(code + 20); // JS code.
414 :
415 5 : register_state.sp = stack;
416 5 : register_state.fp = stack + 5;
417 :
418 : // Put the current PC inside of the code range so it looks valid.
419 5 : register_state.pc = code + 30;
420 :
421 : // Unwind will fail because stack[9] FP points outside of the stack.
422 : bool unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
423 5 : stack_base);
424 5 : CHECK(!unwound);
425 :
426 : // Change the return address so that it is not in range. We will not range
427 : // check the stack[9] FP value because we have finished unwinding and the
428 : // contents of rbp does not necessarily have to be the FP in this case.
429 5 : stack[10] = 202;
430 : unwound = v8::Unwinder::TryUnwindV8Frames(unwind_state, ®ister_state,
431 5 : stack_base);
432 5 : CHECK(unwound);
433 5 : }
434 :
435 26644 : TEST(PCIsInV8_BadState_Fail) {
436 5 : UnwindState unwind_state;
437 : void* pc = nullptr;
438 :
439 5 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
440 5 : }
441 :
442 26644 : TEST(PCIsInV8_ValidStateNullPC_Fail) {
443 5 : LocalContext env;
444 5 : v8::Isolate* isolate = env->GetIsolate();
445 :
446 5 : UnwindState unwind_state = isolate->GetUnwindState();
447 : void* pc = nullptr;
448 :
449 5 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
450 5 : }
451 :
452 10 : void TestRangeBoundaries(const UnwindState& unwind_state, byte* range_start,
453 : size_t range_length) {
454 10 : void* pc = range_start - 1;
455 10 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
456 : pc = range_start;
457 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
458 10 : pc = range_start + 1;
459 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
460 10 : pc = range_start + range_length - 1;
461 10 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
462 10 : pc = range_start + range_length;
463 10 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
464 10 : pc = range_start + range_length + 1;
465 10 : CHECK(!v8::Unwinder::PCIsInV8(unwind_state, pc));
466 10 : }
467 :
468 26644 : TEST(PCIsInV8_InCodeOrEmbeddedRange) {
469 5 : LocalContext env;
470 5 : v8::Isolate* isolate = env->GetIsolate();
471 :
472 5 : UnwindState unwind_state = isolate->GetUnwindState();
473 :
474 : byte* code_range_start = const_cast<byte*>(
475 5 : reinterpret_cast<const byte*>(unwind_state.code_range.start));
476 5 : size_t code_range_length = unwind_state.code_range.length_in_bytes;
477 5 : TestRangeBoundaries(unwind_state, code_range_start, code_range_length);
478 :
479 : byte* embedded_range_start = const_cast<byte*>(
480 5 : reinterpret_cast<const byte*>(unwind_state.embedded_code_range.start));
481 : size_t embedded_range_length =
482 5 : unwind_state.embedded_code_range.length_in_bytes;
483 : TestRangeBoundaries(unwind_state, embedded_range_start,
484 5 : embedded_range_length);
485 5 : }
486 :
487 : // PCIsInV8 doesn't check if the PC is in JSEntry directly. It's assumed that
488 : // the CodeRange or EmbeddedCodeRange contain JSEntry.
489 26644 : TEST(PCIsInV8_InJSEntryRange) {
490 5 : LocalContext env;
491 5 : v8::Isolate* isolate = env->GetIsolate();
492 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
493 :
494 5 : UnwindState unwind_state = isolate->GetUnwindState();
495 :
496 5 : Code js_entry = i_isolate->heap()->builtin(Builtins::kJSEntry);
497 5 : byte* start = reinterpret_cast<byte*>(js_entry->InstructionStart());
498 5 : size_t length = js_entry->InstructionSize();
499 :
500 : void* pc = start;
501 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
502 5 : pc = start + 1;
503 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
504 5 : pc = start + length - 1;
505 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
506 5 : }
507 :
508 : // Large code objects can be allocated in large object space. Check that this is
509 : // inside the CodeRange.
510 26644 : TEST(PCIsInV8_LargeCodeObject) {
511 5 : FLAG_allow_natives_syntax = true;
512 5 : LocalContext env;
513 5 : v8::Isolate* isolate = env->GetIsolate();
514 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
515 : HandleScope scope(i_isolate);
516 :
517 5 : UnwindState unwind_state = isolate->GetUnwindState();
518 :
519 : // Create a big function that ends up in CODE_LO_SPACE.
520 : const int instruction_size = Page::kPageSize + 1;
521 : STATIC_ASSERT(instruction_size > kMaxRegularHeapObjectSize);
522 5 : std::unique_ptr<byte[]> instructions(new byte[instruction_size]);
523 :
524 5 : CodeDesc desc;
525 5 : desc.buffer = instructions.get();
526 5 : desc.buffer_size = instruction_size;
527 5 : desc.instr_size = instruction_size;
528 : desc.reloc_size = 0;
529 : desc.constant_pool_size = 0;
530 : desc.unwinding_info = nullptr;
531 : desc.unwinding_info_size = 0;
532 : desc.origin = nullptr;
533 : Handle<Object> self_ref;
534 : Handle<Code> foo_code =
535 5 : i_isolate->factory()->NewCode(desc, Code::WASM_FUNCTION, self_ref);
536 :
537 10 : CHECK(i_isolate->heap()->InSpace(*foo_code, CODE_LO_SPACE));
538 10 : byte* start = reinterpret_cast<byte*>(foo_code->InstructionStart());
539 :
540 : void* pc = start;
541 5 : CHECK(v8::Unwinder::PCIsInV8(unwind_state, pc));
542 5 : }
543 :
544 : } // namespace test_unwinder
545 : } // namespace internal
546 79917 : } // namespace v8
|