LCOV - code coverage report
Current view: top level - test/cctest - test-unwinder.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 286 286 100.0 %
Date: 2019-01-20 Functions: 18 18 100.0 %

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

Generated by: LCOV version 1.10