LCOV - code coverage report
Current view: top level - test/cctest/wasm - test-wasm-stack.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 80 80 100.0 %
Date: 2019-04-19 Functions: 13 14 92.9 %

          Line data    Source code
       1             : // Copyright 2016 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 "src/api-inl.h"
       6             : #include "src/assembler-inl.h"
       7             : #include "test/cctest/cctest.h"
       8             : #include "test/cctest/compiler/value-helper.h"
       9             : #include "test/cctest/wasm/wasm-run-utils.h"
      10             : #include "test/common/wasm/test-signatures.h"
      11             : #include "test/common/wasm/wasm-macro-gen.h"
      12             : 
      13             : namespace v8 {
      14             : namespace internal {
      15             : namespace wasm {
      16             : namespace test_wasm_stack {
      17             : 
      18             : using v8::Local;
      19             : using v8::Utils;
      20             : 
      21             : namespace {
      22             : 
      23             : #define CHECK_CSTREQ(exp, found)                                           \
      24             :   do {                                                                     \
      25             :     const char* exp_ = (exp);                                              \
      26             :     const char* found_ = (found);                                          \
      27             :     DCHECK_NOT_NULL(exp);                                                  \
      28             :     if (V8_UNLIKELY(found_ == nullptr || strcmp(exp_, found_) != 0)) {     \
      29             :       V8_Fatal(__FILE__, __LINE__,                                         \
      30             :                "Check failed: (%s) != (%s) ('%s' vs '%s').", #exp, #found, \
      31             :                exp_, found_ ? found_ : "<null>");                          \
      32             :     }                                                                      \
      33             :   } while (false)
      34             : 
      35          36 : void PrintStackTrace(v8::Isolate* isolate, v8::Local<v8::StackTrace> stack) {
      36          36 :   printf("Stack Trace (length %d):\n", stack->GetFrameCount());
      37         168 :   for (int i = 0, e = stack->GetFrameCount(); i != e; ++i) {
      38         132 :     v8::Local<v8::StackFrame> frame = stack->GetFrame(isolate, i);
      39         132 :     v8::Local<v8::String> script = frame->GetScriptName();
      40         132 :     v8::Local<v8::String> func = frame->GetFunctionName();
      41         396 :     printf(
      42             :         "[%d] (%s) %s:%d:%d\n", i,
      43         132 :         script.IsEmpty() ? "<null>" : *v8::String::Utf8Value(isolate, script),
      44         264 :         func.IsEmpty() ? "<null>" : *v8::String::Utf8Value(isolate, func),
      45             :         frame->GetLineNumber(), frame->GetColumn());
      46             :   }
      47          36 : }
      48             : 
      49             : struct ExceptionInfo {
      50             :   const char* func_name;
      51             :   int line_nr;  // 1-based
      52             :   int column;   // 1-based
      53             : };
      54             : 
      55             : template <int N>
      56          36 : void CheckExceptionInfos(v8::internal::Isolate* i_isolate, Handle<Object> exc,
      57             :                          const ExceptionInfo (&excInfos)[N]) {
      58             :   // Check that it's indeed an Error object.
      59          36 :   CHECK(exc->IsJSError());
      60             : 
      61             :   v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
      62             : 
      63             :   // Extract stack frame from the exception.
      64          36 :   Local<v8::Value> localExc = Utils::ToLocal(exc);
      65          36 :   v8::Local<v8::StackTrace> stack = v8::Exception::GetStackTrace(localExc);
      66          36 :   PrintStackTrace(v8_isolate, stack);
      67          36 :   CHECK(!stack.IsEmpty());
      68          36 :   CHECK_EQ(N, stack->GetFrameCount());
      69             : 
      70         300 :   for (int frameNr = 0; frameNr < N; ++frameNr) {
      71         132 :     v8::Local<v8::StackFrame> frame = stack->GetFrame(v8_isolate, frameNr);
      72         396 :     v8::String::Utf8Value funName(v8_isolate, frame->GetFunctionName());
      73         132 :     CHECK_CSTREQ(excInfos[frameNr].func_name, *funName);
      74             :     // Line and column are 1-based in v8::StackFrame, just as in ExceptionInfo.
      75         132 :     CHECK_EQ(excInfos[frameNr].line_nr, frame->GetLineNumber());
      76         132 :     CHECK_EQ(excInfos[frameNr].column, frame->GetColumn());
      77             :   }
      78             : 
      79          36 :   CheckComputeLocation(i_isolate, exc, excInfos[0]);
      80          36 : }
      81             : 
      82          36 : void CheckComputeLocation(v8::internal::Isolate* i_isolate, Handle<Object> exc,
      83             :                           const ExceptionInfo& topLocation) {
      84          36 :   MessageLocation loc;
      85          36 :   CHECK(i_isolate->ComputeLocationFromStackTrace(&loc, exc));
      86             :   printf("loc start: %d, end: %d\n", loc.start_pos(), loc.end_pos());
      87          36 :   Handle<JSMessageObject> message = i_isolate->CreateMessage(exc, nullptr);
      88         108 :   printf("msg start: %d, end: %d, line: %d, col: %d\n",
      89             :          message->start_position(), message->end_position(),
      90             :          message->GetLineNumber(), message->GetColumnNumber());
      91          36 :   CHECK_EQ(loc.start_pos(), message->start_position());
      92          36 :   CHECK_EQ(loc.end_pos(), message->end_position());
      93             :   // In the message, the line is 1-based, but the column is 0-based.
      94          36 :   CHECK_EQ(topLocation.line_nr, message->GetLineNumber());
      95          36 :   CHECK_LE(1, topLocation.column);
      96          36 :   CHECK_EQ(topLocation.column - 1, message->GetColumnNumber());
      97          36 : }
      98             : 
      99             : #undef CHECK_CSTREQ
     100             : 
     101             : }  // namespace
     102             : 
     103             : // Call from JS to wasm to JS and throw an Error from JS.
     104       26680 : WASM_EXEC_TEST(CollectDetailedWasmStack_ExplicitThrowFromJs) {
     105          12 :   TestSignatures sigs;
     106             :   HandleScope scope(CcTest::InitIsolateOnce());
     107             :   const char* source =
     108             :       "(function js() {\n function a() {\n throw new Error(); };\n a(); })";
     109             :   Handle<JSFunction> js_function =
     110             :       Handle<JSFunction>::cast(v8::Utils::OpenHandle(
     111             :           *v8::Local<v8::Function>::Cast(CompileRun(source))));
     112          12 :   ManuallyImportedJSFunction import = {sigs.v_v(), js_function};
     113             :   uint32_t js_throwing_index = 0;
     114          24 :   WasmRunner<void> r(execution_tier, &import);
     115             : 
     116             :   // Add a nop such that we don't always get position 1.
     117          12 :   BUILD(r, WASM_NOP, WASM_CALL_FUNCTION0(js_throwing_index));
     118          12 :   uint32_t wasm_index_1 = r.function()->func_index;
     119             : 
     120          12 :   WasmFunctionCompiler& f2 = r.NewFunction<void>("call_main");
     121          12 :   BUILD(f2, WASM_CALL_FUNCTION0(wasm_index_1));
     122             :   uint32_t wasm_index_2 = f2.function_index();
     123             : 
     124          12 :   Handle<JSFunction> js_wasm_wrapper = r.builder().WrapCode(wasm_index_2);
     125             : 
     126             :   Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
     127             :       v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
     128             :           CompileRun("(function callFn(fn) { fn(); })"))));
     129             : 
     130             :   Isolate* isolate = js_wasm_wrapper->GetIsolate();
     131             :   isolate->SetCaptureStackTraceForUncaughtExceptions(true, 10,
     132          12 :                                                      v8::StackTrace::kOverview);
     133          24 :   Handle<Object> global(isolate->context()->global_object(), isolate);
     134          12 :   MaybeHandle<Object> maybe_exc;
     135             :   Handle<Object> args[] = {js_wasm_wrapper};
     136             :   MaybeHandle<Object> returnObjMaybe =
     137             :       Execution::TryCall(isolate, js_trampoline, global, 1, args,
     138          12 :                          Execution::MessageHandling::kReport, &maybe_exc);
     139          12 :   CHECK(returnObjMaybe.is_null());
     140             : 
     141             :   // Line and column are 1-based, so add 1 for the expected wasm output.
     142             :   ExceptionInfo expected_exceptions[] = {
     143             :       {"a", 3, 8},                                           // -
     144             :       {"js", 4, 2},                                          // -
     145          12 :       {"main", static_cast<int>(wasm_index_1) + 1, 3},       // -
     146          12 :       {"call_main", static_cast<int>(wasm_index_2) + 1, 2},  // -
     147             :       {"callFn", 1, 24}                                      // -
     148          36 :   };
     149             :   CheckExceptionInfos(isolate, maybe_exc.ToHandleChecked(),
     150          12 :                       expected_exceptions);
     151          12 : }
     152             : 
     153             : // Trigger a trap in wasm, stack should be JS -> wasm -> wasm.
     154       26672 : WASM_EXEC_TEST(CollectDetailedWasmStack_WasmError) {
     155          56 :   for (int pos_shift = 0; pos_shift < 3; ++pos_shift) {
     156             :     // Test a position with 1, 2 or 3 bytes needed to represent it.
     157          24 :     int unreachable_pos = 1 << (8 * pos_shift);
     158          24 :     TestSignatures sigs;
     159             :     // Create a WasmRunner with stack checks and traps enabled.
     160             :     WasmRunner<int> r(execution_tier, nullptr, "main",
     161          48 :                       kRuntimeExceptionSupport);
     162             : 
     163          24 :     std::vector<byte> code(unreachable_pos + 1, kExprNop);
     164          48 :     code[unreachable_pos] = kExprUnreachable;
     165          24 :     r.Build(code.data(), code.data() + code.size());
     166             : 
     167          24 :     uint32_t wasm_index_1 = r.function()->func_index;
     168             : 
     169          24 :     WasmFunctionCompiler& f2 = r.NewFunction<int>("call_main");
     170          24 :     BUILD(f2, WASM_CALL_FUNCTION0(0));
     171             :     uint32_t wasm_index_2 = f2.function_index();
     172             : 
     173          24 :     Handle<JSFunction> js_wasm_wrapper = r.builder().WrapCode(wasm_index_2);
     174             : 
     175             :     Handle<JSFunction> js_trampoline = Handle<JSFunction>::cast(
     176             :         v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
     177             :             CompileRun("(function callFn(fn) { fn(); })"))));
     178             : 
     179             :     Isolate* isolate = js_wasm_wrapper->GetIsolate();
     180             :     isolate->SetCaptureStackTraceForUncaughtExceptions(
     181          24 :         true, 10, v8::StackTrace::kOverview);
     182          48 :     Handle<Object> global(isolate->context()->global_object(), isolate);
     183          24 :     MaybeHandle<Object> maybe_exc;
     184             :     Handle<Object> args[] = {js_wasm_wrapper};
     185             :     MaybeHandle<Object> maybe_return_obj =
     186             :         Execution::TryCall(isolate, js_trampoline, global, 1, args,
     187          24 :                            Execution::MessageHandling::kReport, &maybe_exc);
     188          24 :     CHECK(maybe_return_obj.is_null());
     189          24 :     Handle<Object> exception = maybe_exc.ToHandleChecked();
     190             : 
     191             :     static constexpr int kMainLocalsLength = 1;
     192             :     // Line and column are 1-based, so add 1 for the expected wasm output.
     193          24 :     const int expected_main_pos = unreachable_pos + kMainLocalsLength + 1;
     194             :     ExceptionInfo expected_exceptions[] = {
     195          24 :         {"main", static_cast<int>(wasm_index_1) + 1, expected_main_pos},  // -
     196          24 :         {"call_main", static_cast<int>(wasm_index_2) + 1, 2},             // -
     197             :         {"callFn", 1, 24}                                                 //-
     198          72 :     };
     199          24 :     CheckExceptionInfos(isolate, exception, expected_exceptions);
     200             :   }
     201           8 : }
     202             : 
     203             : }  // namespace test_wasm_stack
     204             : }  // namespace wasm
     205             : }  // namespace internal
     206       79968 : }  // namespace v8

Generated by: LCOV version 1.10