18b112d2025046f85ef7f6be087c6129c872ebad2Ben Murdoch// Copyright 2011 the V8 project authors. All rights reserved.
29ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// Redistribution and use in source and binary forms, with or without
39ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// modification, are permitted provided that the following conditions are
49ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// met:
59ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//
69ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//     * Redistributions of source code must retain the above copyright
79ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//       notice, this list of conditions and the following disclaimer.
89ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//     * Redistributions in binary form must reproduce the above
99ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//       copyright notice, this list of conditions and the following
109ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//       disclaimer in the documentation and/or other materials provided
119ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//       with the distribution.
129ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//     * Neither the name of Google Inc. nor the names of its
139ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//       contributors may be used to endorse or promote products derived
149ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//       from this software without specific prior written permission.
159ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick//
169ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
179ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
189ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
199ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
209ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
219ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
229ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
239ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
259ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
269ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block//
28a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block// Tests of profiler-related functions from log.h
29a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
30a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#include <stdlib.h>
31a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
32b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/v8.h"
33b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
34b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/api.h"
35b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/codegen.h"
36b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/disassembler.h"
37b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/isolate.h"
38b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/log.h"
39b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/sampler.h"
40b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "src/vm-state-inl.h"
41b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "test/cctest/cctest.h"
42b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch#include "test/cctest/trace-extension.h"
43a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
44a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::Function;
45a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::Local;
46a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::Object;
47a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::Script;
48a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::String;
49a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::Value;
50a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
51a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::internal::byte;
52a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::internal::Address;
53a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::internal::Handle;
5444f0eee88ff00398ff7f715fab053374d808c90dSteve Blockusing v8::internal::Isolate;
55a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::internal::JSFunction;
56a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockusing v8::internal::TickSample;
57a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
58a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
59e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdochstatic bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
60e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch  i::Code* code = function->code();
61e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch  return code->contains(addr);
62e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch}
63e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch
64b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
65b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochstatic bool IsAddressWithinFuncCode(v8::Local<v8::Context> context,
66b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                    const char* func_name,
67b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                    Address addr) {
68b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Local<v8::Value> func = context->Global()->Get(v8_str(func_name));
69e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch  CHECK(func->IsFunction());
70e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch  JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
71e0cee9b3ed82e2391fd85d118aeaa4ea361c687dBen Murdoch  return IsAddressWithinFuncCode(js_func, addr);
72a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
73a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
74a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
759ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// This C++ function is called as a constructor, to grab the frame pointer
769ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// from the calling function.  When this function runs, the stack contains
779ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// a C_Entry frame and a Construct frame above the calling function's frame.
78b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochstatic void construct_call(const v8::FunctionCallbackInfo<v8::Value>& args) {
79b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
80b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::StackFrameIterator frame_iterator(isolate);
819ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  CHECK(frame_iterator.frame()->is_exit());
829ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  frame_iterator.Advance();
839ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  CHECK(frame_iterator.frame()->is_construct());
849ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  frame_iterator.Advance();
859ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  i::StackFrame* calling_frame = frame_iterator.frame();
869ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  CHECK(calling_frame->is_java_script());
879ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick
889ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick#if defined(V8_HOST_ARCH_32_BIT)
890d5e116f6aee03185f237311a943491bb079a768Kristian Monsen  int32_t low_bits = reinterpret_cast<int32_t>(calling_frame->fp());
909ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  args.This()->Set(v8_str("low_bits"), v8_num(low_bits >> 1));
919ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick#elif defined(V8_HOST_ARCH_64_BIT)
920d5e116f6aee03185f237311a943491bb079a768Kristian Monsen  uint64_t fp = reinterpret_cast<uint64_t>(calling_frame->fp());
93f87a203d89e1bbb6708282e0b64dbd13d59b723dBen Murdoch  int32_t low_bits = static_cast<int32_t>(fp & 0xffffffff);
94f87a203d89e1bbb6708282e0b64dbd13d59b723dBen Murdoch  int32_t high_bits = static_cast<int32_t>(fp >> 32);
959ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  args.This()->Set(v8_str("low_bits"), v8_num(low_bits));
969ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  args.This()->Set(v8_str("high_bits"), v8_num(high_bits));
979ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick#else
989ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick#error Host architecture is neither 32-bit nor 64-bit.
999ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick#endif
100b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  args.GetReturnValue().Set(args.This());
1019ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick}
102a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
103a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
1049ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// Use the API to create a JSFunction object that calls the above C++ function.
105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochvoid CreateFramePointerGrabberConstructor(v8::Local<v8::Context> context,
106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                          const char* constructor_name) {
1079ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick    Local<v8::FunctionTemplate> constructor_template =
108b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        v8::FunctionTemplate::New(context->GetIsolate(), construct_call);
1099ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick    constructor_template->SetClassName(v8_str("FPGrabber"));
1109ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick    Local<Function> fun = constructor_template->GetFunction();
111b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    context->Global()->Set(v8_str(constructor_name), fun);
1129ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick}
113a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
114a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
115a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block// Creates a global function named 'func_name' that calls the tracing
116a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block// function 'trace_func_name' with an actual EBP register value,
1179ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick// encoded as one or two Smis.
118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochstatic void CreateTraceCallerFunction(v8::Local<v8::Context> context,
119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                                      const char* func_name,
120a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block                                      const char* trace_func_name) {
121a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  i::EmbeddedVector<char, 256> trace_call_buf;
122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::SNPrintF(trace_call_buf,
123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch              "function %s() {"
124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch              "  fp = new FPGrabber();"
125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch              "  %s(fp.low_bits, fp.high_bits);"
126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch              "}",
127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch              func_name, trace_func_name);
1289ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick
1299ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  // Create the FPGrabber function, which grabs the caller's frame pointer
1309ac36c9faca11611ada13b4054edbaa0738661d0Iain Merrick  // when called as a constructor.
131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CreateFramePointerGrabberConstructor(context, "FPGrabber");
132a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
133a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  // Compile the script.
1345913587db4c6bab03d97bfe44b06289fd6d7270dJohn Reck  CompileRun(trace_call_buf.start());
135a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
136a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
137a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
1386ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// This test verifies that stack tracing works when called during
1396ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// execution of a native function called from JS code. In this case,
140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack
1416ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// walking.
142a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockTEST(CFromJSStackTrace) {
1438b112d2025046f85ef7f6be087c6129c872ebad2Ben Murdoch  // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test.
1448b112d2025046f85ef7f6be087c6129c872ebad2Ben Murdoch  i::FLAG_use_inlining = false;
1458b112d2025046f85ef7f6be087c6129c872ebad2Ben Murdoch
146a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  TickSample sample;
147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::TraceExtension::InitTraceEnv(&sample);
148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::HandleScope scope(CcTest::isolate());
150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
151b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Context::Scope context_scope(context);
152a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
1536ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // Create global function JSFuncDoTrace which calls
1546ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // extension function trace() with the current frame pointer value.
155b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CreateTraceCallerFunction(context, "JSFuncDoTrace", "trace");
1566ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  Local<Value> result = CompileRun(
157a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "function JSTrace() {"
158a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "         JSFuncDoTrace();"
159a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "};\n"
1606ded16be15dd865a9b21ea304d5273c8be299c87Steve Block      "JSTrace();\n"
1616ded16be15dd865a9b21ea304d5273c8be299c87Steve Block      "true;");
1626ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  CHECK(!result.IsEmpty());
1636ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // When stack tracer is invoked, the stack should look as follows:
1646ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // script [JS]
1656ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //   JSTrace() [JS]
1666ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //     JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
1670d5e116f6aee03185f237311a943491bb079a768Kristian Monsen  //       trace(EBP) [native (extension)]
1686ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //         DoTrace(EBP) [native]
169b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  //           TickSample::Trace
170b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
17144f0eee88ff00398ff7f715fab053374d808c90dSteve Block  CHECK(sample.has_external_callback);
172b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK_EQ(FUNCTION_ADDR(i::TraceExtension::Trace), sample.external_callback);
173b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
1746ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
175b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  unsigned base = 0;
176b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch  CHECK_GT(sample.frames_count, base + 1);
1773ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK(IsAddressWithinFuncCode(
179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      context, "JSFuncDoTrace", sample.stack[base + 0]));
180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 1]));
181a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
182a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
183a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
1846ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// This test verifies that stack tracing works when called during
185b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// execution of JS code. However, as calling TickSample::Trace requires
1866ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// entering native code, we can only emulate pure JS by erasing
187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame
1886ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// pointer value as a starting point for stack walking.
189a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockTEST(PureJSStackTrace) {
190b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch  // This test does not pass with inlining enabled since inlined functions
191b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch  // don't appear in the stack trace.
192b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch  i::FLAG_use_inlining = false;
193b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
194a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  TickSample sample;
195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::TraceExtension::InitTraceEnv(&sample);
196b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
197b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::HandleScope scope(CcTest::isolate());
198b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
199b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Context::Scope context_scope(context);
200a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
2016ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // Create global function JSFuncDoTrace which calls
2026ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // extension function js_trace() with the current frame pointer value.
203b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CreateTraceCallerFunction(context, "JSFuncDoTrace", "js_trace");
2046ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  Local<Value> result = CompileRun(
205a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "function JSTrace() {"
206a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "         JSFuncDoTrace();"
207a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "};\n"
208a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "function OuterJSTrace() {"
209a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "         JSTrace();"
210a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      "};\n"
2116ded16be15dd865a9b21ea304d5273c8be299c87Steve Block      "OuterJSTrace();\n"
2126ded16be15dd865a9b21ea304d5273c8be299c87Steve Block      "true;");
2136ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  CHECK(!result.IsEmpty());
2146ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // When stack tracer is invoked, the stack should look as follows:
2156ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  // script [JS]
2166ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //   OuterJSTrace() [JS]
2176ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //     JSTrace() [JS]
2180d5e116f6aee03185f237311a943491bb079a768Kristian Monsen  //       JSFuncDoTrace() [JS]
2190d5e116f6aee03185f237311a943491bb079a768Kristian Monsen  //         js_trace(EBP) [native (extension)]
2206ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //           DoTraceHideCEntryFPAddress(EBP) [native]
221b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  //             TickSample::Trace
2226ded16be15dd865a9b21ea304d5273c8be299c87Steve Block  //
223b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
22444f0eee88ff00398ff7f715fab053374d808c90dSteve Block  CHECK(sample.has_external_callback);
225b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK_EQ(FUNCTION_ADDR(i::TraceExtension::JSTrace), sample.external_callback);
226b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch
227a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
228b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  unsigned base = 0;
229b0fe1620dcb4135ac3ab2d66ff93072373911299Ben Murdoch  CHECK_GT(sample.frames_count, base + 1);
230b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 0]));
231b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK(IsAddressWithinFuncCode(
232b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      context, "OuterJSTrace", sample.stack[base + 1]));
233a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
234a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
235a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
236d0582a6c46733687d045e4188a1bcd0123c758a1Steve Blockstatic void CFuncDoTrace(byte dummy_parameter) {
237a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  Address fp;
238a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#ifdef __GNUC__
239a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  fp = reinterpret_cast<Address>(__builtin_frame_address(0));
240d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block#elif defined _MSC_VER
241d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  // Approximate a frame pointer address. We compile without base pointers,
242d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  // so we can't trust ebp/rbp.
243d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  fp = &dummy_parameter - 2 * sizeof(void*);  // NOLINT
244d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block#else
245d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block#error Unexpected platform.
246a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#endif
247b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::TraceExtension::DoTrace(fp);
248a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
249a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
250a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
251a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockstatic int CFunc(int depth) {
252a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  if (depth <= 0) {
253d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    CFuncDoTrace(0);
254a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return 0;
255a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  } else {
256a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return CFunc(depth - 1) + 1;
257a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  }
258a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
259a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
260a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
2616ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// This test verifies that stack tracing doesn't crash when called on
262b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// pure native code. TickSample::Trace only unrolls JS code, so we can't
2636ded16be15dd865a9b21ea304d5273c8be299c87Steve Block// get any meaningful info here.
264a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockTEST(PureCStackTrace) {
265a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  TickSample sample;
266b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  i::TraceExtension::InitTraceEnv(&sample);
267b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::HandleScope scope(CcTest::isolate());
268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
269b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Context::Scope context_scope(context);
270a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  // Check that sampler doesn't crash
271a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  CHECK_EQ(10, CFunc(10));
272a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
273a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
274a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
275a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockTEST(JsEntrySp) {
276b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::HandleScope scope(CcTest::isolate());
277b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
278b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  v8::Context::Scope context_scope(context);
279b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK_EQ(0, i::TraceExtension::GetJsEntrySp());
280a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  CompileRun("a = 1; b = a + 1;");
281b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK_EQ(0, i::TraceExtension::GetJsEntrySp());
282a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  CompileRun("js_entry_sp();");
283b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK_EQ(0, i::TraceExtension::GetJsEntrySp());
284a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  CompileRun("js_entry_sp_level2();");
285b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  CHECK_EQ(0, i::TraceExtension::GetJsEntrySp());
286a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block}
287