blob: df0806f8b00c0069752e4c771461dd2467b7b70f [file] [log] [blame]
Iain Merrick9ac36c92010-09-13 15:29:50 +01001// Copyright 2010 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.
Steve Blocka7e24c12009-10-30 11:49:00 +000027//
28// Tests of profiler-related functions from log.h
29
30#ifdef ENABLE_LOGGING_AND_PROFILING
31
32#include <stdlib.h>
33
34#include "v8.h"
35
Ben Murdoche0cee9b2011-05-25 10:26:03 +010036#include "api.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000037#include "codegen.h"
38#include "log.h"
Steve Block44f0eee2011-05-26 01:26:41 +010039#include "isolate.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000040#include "cctest.h"
41#include "disassembler.h"
42#include "register-allocator-inl.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010043#include "vm-state-inl.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000044
45using v8::Function;
46using v8::Local;
47using v8::Object;
48using v8::Script;
49using v8::String;
50using v8::Value;
51
52using v8::internal::byte;
53using v8::internal::Address;
54using v8::internal::Handle;
Steve Block44f0eee2011-05-26 01:26:41 +010055using v8::internal::Isolate;
Steve Blocka7e24c12009-10-30 11:49:00 +000056using v8::internal::JSFunction;
57using v8::internal::StackTracer;
58using v8::internal::TickSample;
Steve Blocka7e24c12009-10-30 11:49:00 +000059
60namespace i = v8::internal;
61
62
63static v8::Persistent<v8::Context> env;
64
65
66static struct {
67 TickSample* sample;
68} trace_env = { NULL };
69
70
71static void InitTraceEnv(TickSample* sample) {
72 trace_env.sample = sample;
73}
74
75
76static void DoTrace(Address fp) {
Leon Clarked91b9f72010-01-27 17:25:45 +000077 trace_env.sample->fp = fp;
Steve Blocka7e24c12009-10-30 11:49:00 +000078 // sp is only used to define stack high bound
79 trace_env.sample->sp =
Leon Clarked91b9f72010-01-27 17:25:45 +000080 reinterpret_cast<Address>(trace_env.sample) - 10240;
Steve Block44f0eee2011-05-26 01:26:41 +010081 StackTracer::Trace(Isolate::Current(), trace_env.sample);
Steve Blocka7e24c12009-10-30 11:49:00 +000082}
83
84
85// Hide c_entry_fp to emulate situation when sampling is done while
86// pure JS code is being executed
87static void DoTraceHideCEntryFPAddress(Address fp) {
Steve Block44f0eee2011-05-26 01:26:41 +010088 v8::internal::Address saved_c_frame_fp =
89 *(Isolate::Current()->c_entry_fp_address());
Steve Blocka7e24c12009-10-30 11:49:00 +000090 CHECK(saved_c_frame_fp);
Steve Block44f0eee2011-05-26 01:26:41 +010091 *(Isolate::Current()->c_entry_fp_address()) = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +000092 DoTrace(fp);
Steve Block44f0eee2011-05-26 01:26:41 +010093 *(Isolate::Current()->c_entry_fp_address()) = saved_c_frame_fp;
Steve Blocka7e24c12009-10-30 11:49:00 +000094}
95
96
Steve Blocka7e24c12009-10-30 11:49:00 +000097// --- T r a c e E x t e n s i o n ---
98
99class TraceExtension : public v8::Extension {
100 public:
101 TraceExtension() : v8::Extension("v8/trace", kSource) { }
102 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
103 v8::Handle<String> name);
104 static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
105 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
106 static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args);
107 static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args);
108 private:
109 static Address GetFP(const v8::Arguments& args);
110 static const char* kSource;
111};
112
113
114const char* TraceExtension::kSource =
115 "native function trace();"
116 "native function js_trace();"
117 "native function js_entry_sp();"
118 "native function js_entry_sp_level2();";
119
120v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
121 v8::Handle<String> name) {
122 if (name->Equals(String::New("trace"))) {
123 return v8::FunctionTemplate::New(TraceExtension::Trace);
124 } else if (name->Equals(String::New("js_trace"))) {
125 return v8::FunctionTemplate::New(TraceExtension::JSTrace);
126 } else if (name->Equals(String::New("js_entry_sp"))) {
127 return v8::FunctionTemplate::New(TraceExtension::JSEntrySP);
128 } else if (name->Equals(String::New("js_entry_sp_level2"))) {
129 return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2);
130 } else {
131 CHECK(false);
132 return v8::Handle<v8::FunctionTemplate>();
133 }
134}
135
136
137Address TraceExtension::GetFP(const v8::Arguments& args) {
Iain Merrick9ac36c92010-09-13 15:29:50 +0100138 // Convert frame pointer from encoding as smis in the arguments to a pointer.
139 CHECK_EQ(2, args.Length()); // Ignore second argument on 32-bit platform.
140#if defined(V8_HOST_ARCH_32_BIT)
Steve Blocka7e24c12009-10-30 11:49:00 +0000141 Address fp = *reinterpret_cast<Address*>(*args[0]);
Iain Merrick9ac36c92010-09-13 15:29:50 +0100142#elif defined(V8_HOST_ARCH_64_BIT)
143 int64_t low_bits = *reinterpret_cast<uint64_t*>(*args[0]) >> 32;
144 int64_t high_bits = *reinterpret_cast<uint64_t*>(*args[1]);
145 Address fp = reinterpret_cast<Address>(high_bits | low_bits);
146#else
147#error Host architecture is neither 32-bit nor 64-bit.
148#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000149 printf("Trace: %p\n", fp);
150 return fp;
151}
152
153
154v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
155 DoTrace(GetFP(args));
156 return v8::Undefined();
157}
158
159
160v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
161 DoTraceHideCEntryFPAddress(GetFP(args));
162 return v8::Undefined();
163}
164
165
166static Address GetJsEntrySp() {
Steve Block44f0eee2011-05-26 01:26:41 +0100167 CHECK_NE(NULL, i::Isolate::Current()->thread_local_top());
168 return Isolate::js_entry_sp(i::Isolate::Current()->thread_local_top());
Steve Blocka7e24c12009-10-30 11:49:00 +0000169}
170
171
172v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) {
173 CHECK_NE(0, GetJsEntrySp());
174 return v8::Undefined();
175}
176
177
Steve Blocka7e24c12009-10-30 11:49:00 +0000178v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2(
179 const v8::Arguments& args) {
180 v8::HandleScope scope;
181 const Address js_entry_sp = GetJsEntrySp();
182 CHECK_NE(0, js_entry_sp);
183 CompileRun("js_entry_sp();");
184 CHECK_EQ(js_entry_sp, GetJsEntrySp());
185 return v8::Undefined();
186}
187
188
189static TraceExtension kTraceExtension;
190v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
191
192
193static void InitializeVM() {
194 if (env.IsEmpty()) {
195 v8::HandleScope scope;
196 const char* extensions[] = { "v8/trace" };
197 v8::ExtensionConfiguration config(1, extensions);
198 env = v8::Context::New(&config);
199 }
200 v8::HandleScope scope;
201 env->Enter();
202}
203
204
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100205static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
206 i::Code* code = function->code();
207 return code->contains(addr);
208}
209
210static bool IsAddressWithinFuncCode(const char* func_name, Address addr) {
211 v8::Local<v8::Value> func = env->Global()->Get(v8_str(func_name));
212 CHECK(func->IsFunction());
213 JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
214 return IsAddressWithinFuncCode(js_func, addr);
Steve Blocka7e24c12009-10-30 11:49:00 +0000215}
216
217
Iain Merrick9ac36c92010-09-13 15:29:50 +0100218// This C++ function is called as a constructor, to grab the frame pointer
219// from the calling function. When this function runs, the stack contains
220// a C_Entry frame and a Construct frame above the calling function's frame.
221static v8::Handle<Value> construct_call(const v8::Arguments& args) {
222 i::StackFrameIterator frame_iterator;
223 CHECK(frame_iterator.frame()->is_exit());
224 frame_iterator.Advance();
225 CHECK(frame_iterator.frame()->is_construct());
226 frame_iterator.Advance();
227 i::StackFrame* calling_frame = frame_iterator.frame();
228 CHECK(calling_frame->is_java_script());
Steve Blocka7e24c12009-10-30 11:49:00 +0000229
Iain Merrick9ac36c92010-09-13 15:29:50 +0100230#if defined(V8_HOST_ARCH_32_BIT)
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100231 int32_t low_bits = reinterpret_cast<int32_t>(calling_frame->fp());
Iain Merrick9ac36c92010-09-13 15:29:50 +0100232 args.This()->Set(v8_str("low_bits"), v8_num(low_bits >> 1));
233#elif defined(V8_HOST_ARCH_64_BIT)
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100234 uint64_t fp = reinterpret_cast<uint64_t>(calling_frame->fp());
Ben Murdochf87a2032010-10-22 12:50:53 +0100235 int32_t low_bits = static_cast<int32_t>(fp & 0xffffffff);
236 int32_t high_bits = static_cast<int32_t>(fp >> 32);
Iain Merrick9ac36c92010-09-13 15:29:50 +0100237 args.This()->Set(v8_str("low_bits"), v8_num(low_bits));
238 args.This()->Set(v8_str("high_bits"), v8_num(high_bits));
239#else
240#error Host architecture is neither 32-bit nor 64-bit.
241#endif
242 return args.This();
243}
Steve Blocka7e24c12009-10-30 11:49:00 +0000244
Steve Blocka7e24c12009-10-30 11:49:00 +0000245
Iain Merrick9ac36c92010-09-13 15:29:50 +0100246// Use the API to create a JSFunction object that calls the above C++ function.
247void CreateFramePointerGrabberConstructor(const char* constructor_name) {
248 Local<v8::FunctionTemplate> constructor_template =
249 v8::FunctionTemplate::New(construct_call);
250 constructor_template->SetClassName(v8_str("FPGrabber"));
251 Local<Function> fun = constructor_template->GetFunction();
252 env->Global()->Set(v8_str(constructor_name), fun);
253}
Steve Blocka7e24c12009-10-30 11:49:00 +0000254
255
256// Creates a global function named 'func_name' that calls the tracing
257// function 'trace_func_name' with an actual EBP register value,
Iain Merrick9ac36c92010-09-13 15:29:50 +0100258// encoded as one or two Smis.
Steve Blocka7e24c12009-10-30 11:49:00 +0000259static void CreateTraceCallerFunction(const char* func_name,
260 const char* trace_func_name) {
261 i::EmbeddedVector<char, 256> trace_call_buf;
Iain Merrick9ac36c92010-09-13 15:29:50 +0100262 i::OS::SNPrintF(trace_call_buf,
John Reck59135872010-11-02 12:39:01 -0700263 "function %s() {"
264 " fp = new FPGrabber();"
265 " %s(fp.low_bits, fp.high_bits);"
266 "}",
267 func_name, trace_func_name);
Iain Merrick9ac36c92010-09-13 15:29:50 +0100268
269 // Create the FPGrabber function, which grabs the caller's frame pointer
270 // when called as a constructor.
271 CreateFramePointerGrabberConstructor("FPGrabber");
Steve Blocka7e24c12009-10-30 11:49:00 +0000272
273 // Compile the script.
John Reck59135872010-11-02 12:39:01 -0700274 CompileRun(trace_call_buf.start());
Steve Blocka7e24c12009-10-30 11:49:00 +0000275}
276
277
Steve Block6ded16b2010-05-10 14:33:55 +0100278// This test verifies that stack tracing works when called during
279// execution of a native function called from JS code. In this case,
Steve Block44f0eee2011-05-26 01:26:41 +0100280// StackTracer uses Isolate::c_entry_fp as a starting point for stack
Steve Block6ded16b2010-05-10 14:33:55 +0100281// walking.
Steve Blocka7e24c12009-10-30 11:49:00 +0000282TEST(CFromJSStackTrace) {
283 TickSample sample;
284 InitTraceEnv(&sample);
285
286 InitializeVM();
287 v8::HandleScope scope;
Steve Block6ded16b2010-05-10 14:33:55 +0100288 // Create global function JSFuncDoTrace which calls
289 // extension function trace() with the current frame pointer value.
Steve Blocka7e24c12009-10-30 11:49:00 +0000290 CreateTraceCallerFunction("JSFuncDoTrace", "trace");
Steve Block6ded16b2010-05-10 14:33:55 +0100291 Local<Value> result = CompileRun(
Steve Blocka7e24c12009-10-30 11:49:00 +0000292 "function JSTrace() {"
293 " JSFuncDoTrace();"
294 "};\n"
Steve Block6ded16b2010-05-10 14:33:55 +0100295 "JSTrace();\n"
296 "true;");
297 CHECK(!result.IsEmpty());
298 // When stack tracer is invoked, the stack should look as follows:
299 // script [JS]
300 // JSTrace() [JS]
301 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100302 // trace(EBP) [native (extension)]
Steve Block6ded16b2010-05-10 14:33:55 +0100303 // DoTrace(EBP) [native]
304 // StackTracer::Trace
Ben Murdochb0fe1622011-05-05 13:52:32 +0100305
Steve Block44f0eee2011-05-26 01:26:41 +0100306 CHECK(sample.has_external_callback);
307 CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100308
Steve Block6ded16b2010-05-10 14:33:55 +0100309 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
Steve Block44f0eee2011-05-26 01:26:41 +0100310 int base = 0;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100311 CHECK_GT(sample.frames_count, base + 1);
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100312 CHECK(IsAddressWithinFuncCode("JSFuncDoTrace", sample.stack[base + 0]));
313 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 1]));
Steve Blocka7e24c12009-10-30 11:49:00 +0000314}
315
316
Steve Block6ded16b2010-05-10 14:33:55 +0100317// This test verifies that stack tracing works when called during
318// execution of JS code. However, as calling StackTracer requires
319// entering native code, we can only emulate pure JS by erasing
Steve Block44f0eee2011-05-26 01:26:41 +0100320// Isolate::c_entry_fp value. In this case, StackTracer uses passed frame
Steve Block6ded16b2010-05-10 14:33:55 +0100321// pointer value as a starting point for stack walking.
Steve Blocka7e24c12009-10-30 11:49:00 +0000322TEST(PureJSStackTrace) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100323 // This test does not pass with inlining enabled since inlined functions
324 // don't appear in the stack trace.
325 i::FLAG_use_inlining = false;
326
Steve Blocka7e24c12009-10-30 11:49:00 +0000327 TickSample sample;
328 InitTraceEnv(&sample);
329
330 InitializeVM();
331 v8::HandleScope scope;
Steve Block6ded16b2010-05-10 14:33:55 +0100332 // Create global function JSFuncDoTrace which calls
333 // extension function js_trace() with the current frame pointer value.
Steve Blocka7e24c12009-10-30 11:49:00 +0000334 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
Steve Block6ded16b2010-05-10 14:33:55 +0100335 Local<Value> result = CompileRun(
Steve Blocka7e24c12009-10-30 11:49:00 +0000336 "function JSTrace() {"
337 " JSFuncDoTrace();"
338 "};\n"
339 "function OuterJSTrace() {"
340 " JSTrace();"
341 "};\n"
Steve Block6ded16b2010-05-10 14:33:55 +0100342 "OuterJSTrace();\n"
343 "true;");
344 CHECK(!result.IsEmpty());
345 // When stack tracer is invoked, the stack should look as follows:
346 // script [JS]
347 // OuterJSTrace() [JS]
348 // JSTrace() [JS]
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100349 // JSFuncDoTrace() [JS]
350 // js_trace(EBP) [native (extension)]
Steve Block6ded16b2010-05-10 14:33:55 +0100351 // DoTraceHideCEntryFPAddress(EBP) [native]
352 // StackTracer::Trace
353 //
Ben Murdochb0fe1622011-05-05 13:52:32 +0100354
Steve Block44f0eee2011-05-26 01:26:41 +0100355 CHECK(sample.has_external_callback);
356 CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100357
Steve Blocka7e24c12009-10-30 11:49:00 +0000358 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
Steve Block44f0eee2011-05-26 01:26:41 +0100359 int base = 0;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100360 CHECK_GT(sample.frames_count, base + 1);
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100361 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 0]));
362 CHECK(IsAddressWithinFuncCode("OuterJSTrace", sample.stack[base + 1]));
Steve Blocka7e24c12009-10-30 11:49:00 +0000363}
364
365
Steve Blockd0582a62009-12-15 09:54:21 +0000366static void CFuncDoTrace(byte dummy_parameter) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000367 Address fp;
368#ifdef __GNUC__
369 fp = reinterpret_cast<Address>(__builtin_frame_address(0));
Steve Blockd0582a62009-12-15 09:54:21 +0000370#elif defined _MSC_VER
371 // Approximate a frame pointer address. We compile without base pointers,
372 // so we can't trust ebp/rbp.
373 fp = &dummy_parameter - 2 * sizeof(void*); // NOLINT
374#else
375#error Unexpected platform.
Steve Blocka7e24c12009-10-30 11:49:00 +0000376#endif
377 DoTrace(fp);
378}
379
380
381static int CFunc(int depth) {
382 if (depth <= 0) {
Steve Blockd0582a62009-12-15 09:54:21 +0000383 CFuncDoTrace(0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000384 return 0;
385 } else {
386 return CFunc(depth - 1) + 1;
387 }
388}
389
390
Steve Block6ded16b2010-05-10 14:33:55 +0100391// This test verifies that stack tracing doesn't crash when called on
392// pure native code. StackTracer only unrolls JS code, so we can't
393// get any meaningful info here.
Steve Blocka7e24c12009-10-30 11:49:00 +0000394TEST(PureCStackTrace) {
395 TickSample sample;
396 InitTraceEnv(&sample);
Steve Block44f0eee2011-05-26 01:26:41 +0100397 InitializeVM();
Steve Blocka7e24c12009-10-30 11:49:00 +0000398 // Check that sampler doesn't crash
399 CHECK_EQ(10, CFunc(10));
400}
401
402
403TEST(JsEntrySp) {
404 InitializeVM();
405 v8::HandleScope scope;
406 CHECK_EQ(0, GetJsEntrySp());
407 CompileRun("a = 1; b = a + 1;");
408 CHECK_EQ(0, GetJsEntrySp());
409 CompileRun("js_entry_sp();");
410 CHECK_EQ(0, GetJsEntrySp());
411 CompileRun("js_entry_sp_level2();");
412 CHECK_EQ(0, GetJsEntrySp());
413}
414
415#endif // ENABLE_LOGGING_AND_PROFILING