LCOV - code coverage report
Current view: top level - test/cctest - test-log-stack-tracer.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 81 81 100.0 %
Date: 2017-10-20 Functions: 11 11 100.0 %

          Line data    Source code
       1             : // Copyright 2011 the V8 project authors. All rights reserved.
       2             : // Redistribution and use in source and binary forms, with or without
       3             : // modification, are permitted provided that the following conditions are
       4             : // met:
       5             : //
       6             : //     * Redistributions of source code must retain the above copyright
       7             : //       notice, this list of conditions and the following disclaimer.
       8             : //     * Redistributions in binary form must reproduce the above
       9             : //       copyright notice, this list of conditions and the following
      10             : //       disclaimer in the documentation and/or other materials provided
      11             : //       with the distribution.
      12             : //     * Neither the name of Google Inc. nor the names of its
      13             : //       contributors may be used to endorse or promote products derived
      14             : //       from this software without specific prior written permission.
      15             : //
      16             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      17             : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      18             : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      19             : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      20             : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      21             : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      22             : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      23             : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      24             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      25             : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      26             : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      27             : //
      28             : // Tests of profiler-related functions from log.h
      29             : 
      30             : #include <stdlib.h>
      31             : 
      32             : #include "include/v8-profiler.h"
      33             : #include "src/api.h"
      34             : #include "src/code-stubs.h"
      35             : #include "src/disassembler.h"
      36             : #include "src/isolate.h"
      37             : #include "src/log.h"
      38             : #include "src/objects-inl.h"
      39             : #include "src/v8.h"
      40             : #include "src/vm-state-inl.h"
      41             : #include "test/cctest/cctest.h"
      42             : #include "test/cctest/trace-extension.h"
      43             : 
      44             : namespace v8 {
      45             : namespace internal {
      46             : 
      47          24 : static bool IsAddressWithinFuncCode(JSFunction* function, void* addr) {
      48             :   Address address = reinterpret_cast<Address>(addr);
      49          24 :   i::AbstractCode* code = function->abstract_code();
      50          24 :   return code->contains(address);
      51             : }
      52             : 
      53          24 : static bool IsAddressWithinFuncCode(v8::Local<v8::Context> context,
      54             :                                     const char* func_name, void* addr) {
      55             :   v8::Local<v8::Value> func =
      56          96 :       context->Global()->Get(context, v8_str(func_name)).ToLocalChecked();
      57          24 :   CHECK(func->IsFunction());
      58             :   JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
      59          24 :   return IsAddressWithinFuncCode(js_func, addr);
      60             : }
      61             : 
      62             : 
      63             : // This C++ function is called as a constructor, to grab the frame pointer
      64             : // from the calling function.  When this function runs, the stack contains
      65             : // a C_Entry frame and a Construct frame above the calling function's frame.
      66          72 : static void construct_call(const v8::FunctionCallbackInfo<v8::Value>& args) {
      67             :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
      68          12 :   i::StackFrameIterator frame_iterator(isolate);
      69          24 :   CHECK(frame_iterator.frame()->is_exit() ||
      70             :         frame_iterator.frame()->is_builtin_exit());
      71          12 :   frame_iterator.Advance();
      72          24 :   CHECK(frame_iterator.frame()->is_construct());
      73          12 :   frame_iterator.Advance();
      74          12 :   if (frame_iterator.frame()->type() == i::StackFrame::STUB) {
      75             :     // Skip over bytecode handler frame.
      76          10 :     frame_iterator.Advance();
      77             :   }
      78          24 :   i::StackFrame* calling_frame = frame_iterator.frame();
      79          12 :   CHECK(calling_frame->is_java_script());
      80             : 
      81          12 :   v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
      82             : #if defined(V8_HOST_ARCH_32_BIT)
      83             :   int32_t low_bits = reinterpret_cast<int32_t>(calling_frame->fp());
      84             :   args.This()
      85             :       ->Set(context, v8_str("low_bits"), v8_num(low_bits >> 1))
      86             :       .FromJust();
      87             : #elif defined(V8_HOST_ARCH_64_BIT)
      88          12 :   uint64_t fp = reinterpret_cast<uint64_t>(calling_frame->fp());
      89          12 :   int32_t low_bits = static_cast<int32_t>(fp & 0xffffffff);
      90          12 :   int32_t high_bits = static_cast<int32_t>(fp >> 32);
      91          36 :   args.This()->Set(context, v8_str("low_bits"), v8_num(low_bits)).FromJust();
      92          36 :   args.This()->Set(context, v8_str("high_bits"), v8_num(high_bits)).FromJust();
      93             : #else
      94             : #error Host architecture is neither 32-bit nor 64-bit.
      95             : #endif
      96             :   args.GetReturnValue().Set(args.This());
      97          12 : }
      98             : 
      99             : 
     100             : // Use the API to create a JSFunction object that calls the above C++ function.
     101          12 : void CreateFramePointerGrabberConstructor(v8::Local<v8::Context> context,
     102             :                                           const char* constructor_name) {
     103             :     Local<v8::FunctionTemplate> constructor_template =
     104          12 :         v8::FunctionTemplate::New(context->GetIsolate(), construct_call);
     105          12 :     constructor_template->SetClassName(v8_str("FPGrabber"));
     106             :     Local<Function> fun =
     107          12 :         constructor_template->GetFunction(context).ToLocalChecked();
     108          48 :     context->Global()->Set(context, v8_str(constructor_name), fun).FromJust();
     109          12 : }
     110             : 
     111             : 
     112             : // Creates a global function named 'func_name' that calls the tracing
     113             : // function 'trace_func_name' with an actual EBP register value,
     114             : // encoded as one or two Smis.
     115          12 : static void CreateTraceCallerFunction(v8::Local<v8::Context> context,
     116             :                                       const char* func_name,
     117             :                                       const char* trace_func_name) {
     118             :   i::EmbeddedVector<char, 256> trace_call_buf;
     119             :   i::SNPrintF(trace_call_buf,
     120             :               "function %s() {"
     121             :               "  fp = new FPGrabber();"
     122             :               "  %s(fp.low_bits, fp.high_bits);"
     123             :               "}",
     124          12 :               func_name, trace_func_name);
     125             : 
     126             :   // Create the FPGrabber function, which grabs the caller's frame pointer
     127             :   // when called as a constructor.
     128          12 :   CreateFramePointerGrabberConstructor(context, "FPGrabber");
     129             : 
     130             :   // Compile the script.
     131          12 :   CompileRun(trace_call_buf.start());
     132          12 : }
     133             : 
     134             : 
     135             : // This test verifies that stack tracing works when called during
     136             : // execution of a native function called from JS code. In this case,
     137             : // TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack
     138             : // walking.
     139       23724 : TEST(CFromJSStackTrace) {
     140             :   // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test.
     141           6 :   i::FLAG_turbo_inlining = false;
     142             : 
     143             :   TickSample sample;
     144           6 :   i::TraceExtension::InitTraceEnv(&sample);
     145             : 
     146           6 :   v8::HandleScope scope(CcTest::isolate());
     147           6 :   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
     148             :   v8::Context::Scope context_scope(context);
     149             : 
     150             :   // Create global function JSFuncDoTrace which calls
     151             :   // extension function trace() with the current frame pointer value.
     152           6 :   CreateTraceCallerFunction(context, "JSFuncDoTrace", "trace");
     153             :   Local<Value> result = CompileRun(
     154             :       "function JSTrace() {"
     155             :       "         JSFuncDoTrace();"
     156             :       "};\n"
     157             :       "JSTrace();\n"
     158             :       "true;");
     159           6 :   CHECK(!result.IsEmpty());
     160             :   // When stack tracer is invoked, the stack should look as follows:
     161             :   // script [JS]
     162             :   //   JSTrace() [JS]
     163             :   //     JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
     164             :   //       trace(EBP) [native (extension)]
     165             :   //         DoTrace(EBP) [native]
     166             :   //           TickSample::Trace
     167             : 
     168           6 :   CHECK(sample.has_external_callback);
     169           6 :   CHECK_EQ(FUNCTION_ADDR(i::TraceExtension::Trace),
     170             :            sample.external_callback_entry);
     171             : 
     172             :   // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
     173             :   unsigned base = 0;
     174           6 :   CHECK_GT(sample.frames_count, base + 1);
     175             : 
     176           6 :   CHECK(IsAddressWithinFuncCode(
     177             :       context, "JSFuncDoTrace", sample.stack[base + 0]));
     178          12 :   CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 1]));
     179           6 : }
     180             : 
     181             : 
     182             : // This test verifies that stack tracing works when called during
     183             : // execution of JS code. However, as calling TickSample::Trace requires
     184             : // entering native code, we can only emulate pure JS by erasing
     185             : // Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame
     186             : // pointer value as a starting point for stack walking.
     187       23724 : TEST(PureJSStackTrace) {
     188             :   // This test does not pass with inlining enabled since inlined functions
     189             :   // don't appear in the stack trace.
     190           6 :   i::FLAG_turbo_inlining = false;
     191             : 
     192             :   TickSample sample;
     193           6 :   i::TraceExtension::InitTraceEnv(&sample);
     194             : 
     195           6 :   v8::HandleScope scope(CcTest::isolate());
     196           6 :   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
     197             :   v8::Context::Scope context_scope(context);
     198             : 
     199             :   // Create global function JSFuncDoTrace which calls
     200             :   // extension function js_trace() with the current frame pointer value.
     201           6 :   CreateTraceCallerFunction(context, "JSFuncDoTrace", "js_trace");
     202             :   Local<Value> result = CompileRun(
     203             :       "function JSTrace() {"
     204             :       "         JSFuncDoTrace();"
     205             :       "};\n"
     206             :       "function OuterJSTrace() {"
     207             :       "         JSTrace();"
     208             :       "};\n"
     209             :       "OuterJSTrace();\n"
     210             :       "true;");
     211           6 :   CHECK(!result.IsEmpty());
     212             :   // When stack tracer is invoked, the stack should look as follows:
     213             :   // script [JS]
     214             :   //   OuterJSTrace() [JS]
     215             :   //     JSTrace() [JS]
     216             :   //       JSFuncDoTrace() [JS]
     217             :   //         js_trace(EBP) [native (extension)]
     218             :   //           DoTraceHideCEntryFPAddress(EBP) [native]
     219             :   //             TickSample::Trace
     220             :   //
     221             : 
     222           6 :   CHECK(sample.has_external_callback);
     223           6 :   CHECK_EQ(FUNCTION_ADDR(i::TraceExtension::JSTrace),
     224             :            sample.external_callback_entry);
     225             : 
     226             :   // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
     227             :   unsigned base = 0;
     228           6 :   CHECK_GT(sample.frames_count, base + 1);
     229           6 :   CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 0]));
     230           6 :   CHECK(IsAddressWithinFuncCode(
     231           6 :       context, "OuterJSTrace", sample.stack[base + 1]));
     232           6 : }
     233             : 
     234             : 
     235             : static void CFuncDoTrace(byte dummy_parameter) {
     236             :   Address fp;
     237             : #if V8_HAS_BUILTIN_FRAME_ADDRESS
     238           6 :   fp = reinterpret_cast<Address>(__builtin_frame_address(0));
     239             : #elif V8_CC_MSVC
     240             :   // Approximate a frame pointer address. We compile without base pointers,
     241             :   // so we can't trust ebp/rbp.
     242             :   fp = &dummy_parameter - 2 * sizeof(void*);  // NOLINT
     243             : #else
     244             : #error Unexpected platform.
     245             : #endif
     246           6 :   i::TraceExtension::DoTrace(fp);
     247             : }
     248             : 
     249             : 
     250             : static int CFunc(int depth) {
     251          66 :   if (depth <= 0) {
     252             :     CFuncDoTrace(0);
     253             :     return 0;
     254             :   } else {
     255          60 :     return CFunc(depth - 1) + 1;
     256             :   }
     257             : }
     258             : 
     259             : 
     260             : // This test verifies that stack tracing doesn't crash when called on
     261             : // pure native code. TickSample::Trace only unrolls JS code, so we can't
     262             : // get any meaningful info here.
     263       23724 : TEST(PureCStackTrace) {
     264             :   TickSample sample;
     265           6 :   i::TraceExtension::InitTraceEnv(&sample);
     266           6 :   v8::HandleScope scope(CcTest::isolate());
     267           6 :   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
     268             :   v8::Context::Scope context_scope(context);
     269             :   // Check that sampler doesn't crash
     270          12 :   CHECK_EQ(10, CFunc(10));
     271           6 : }
     272             : 
     273             : 
     274       23724 : TEST(JsEntrySp) {
     275           6 :   v8::HandleScope scope(CcTest::isolate());
     276           6 :   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
     277             :   v8::Context::Scope context_scope(context);
     278           6 :   CHECK(!i::TraceExtension::GetJsEntrySp());
     279             :   CompileRun("a = 1; b = a + 1;");
     280           6 :   CHECK(!i::TraceExtension::GetJsEntrySp());
     281             :   CompileRun("js_entry_sp();");
     282           6 :   CHECK(!i::TraceExtension::GetJsEntrySp());
     283             :   CompileRun("js_entry_sp_level2();");
     284          12 :   CHECK(!i::TraceExtension::GetJsEntrySp());
     285           6 : }
     286             : 
     287             : }  // namespace internal
     288       71154 : }  // namespace v8

Generated by: LCOV version 1.10