- add third_party src.
[platform/framework/web/crosswalk.git] / src / v8 / test / cctest / test-log-stack-tracer.cc
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 "v8.h"
33
34 #include "api.h"
35 #include "cctest.h"
36 #include "codegen.h"
37 #include "disassembler.h"
38 #include "isolate.h"
39 #include "log.h"
40 #include "sampler.h"
41 #include "vm-state-inl.h"
42
43 using v8::Function;
44 using v8::Local;
45 using v8::Object;
46 using v8::Script;
47 using v8::String;
48 using v8::Value;
49
50 using v8::internal::byte;
51 using v8::internal::Address;
52 using v8::internal::Handle;
53 using v8::internal::Isolate;
54 using v8::internal::JSFunction;
55 using v8::internal::RegisterState;
56 using v8::internal::TickSample;
57
58
59 static struct {
60   TickSample* sample;
61 } trace_env = { NULL };
62
63
64 static void InitTraceEnv(TickSample* sample) {
65   trace_env.sample = sample;
66 }
67
68
69 static void DoTrace(Address fp) {
70   RegisterState regs;
71   regs.fp = fp;
72   // sp is only used to define stack high bound
73   regs.sp =
74       reinterpret_cast<Address>(trace_env.sample) - 10240;
75   trace_env.sample->Init(CcTest::i_isolate(), regs);
76 }
77
78
79 // Hide c_entry_fp to emulate situation when sampling is done while
80 // pure JS code is being executed
81 static void DoTraceHideCEntryFPAddress(Address fp) {
82   v8::internal::Address saved_c_frame_fp =
83       *(CcTest::i_isolate()->c_entry_fp_address());
84   CHECK(saved_c_frame_fp);
85   *(CcTest::i_isolate()->c_entry_fp_address()) = 0;
86   DoTrace(fp);
87   *(CcTest::i_isolate()->c_entry_fp_address()) = saved_c_frame_fp;
88 }
89
90
91 // --- T r a c e   E x t e n s i o n ---
92
93 class TraceExtension : public v8::Extension {
94  public:
95   TraceExtension() : v8::Extension("v8/trace", kSource) { }
96   virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
97       v8::Handle<String> name);
98   static void Trace(const v8::FunctionCallbackInfo<v8::Value>& args);
99   static void JSTrace(const v8::FunctionCallbackInfo<v8::Value>& args);
100   static void JSEntrySP(const v8::FunctionCallbackInfo<v8::Value>& args);
101   static void JSEntrySPLevel2(const v8::FunctionCallbackInfo<v8::Value>& args);
102  private:
103   static Address GetFP(const v8::FunctionCallbackInfo<v8::Value>& args);
104   static const char* kSource;
105 };
106
107
108 const char* TraceExtension::kSource =
109     "native function trace();"
110     "native function js_trace();"
111     "native function js_entry_sp();"
112     "native function js_entry_sp_level2();";
113
114 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
115     v8::Handle<String> name) {
116   if (name->Equals(String::New("trace"))) {
117     return v8::FunctionTemplate::New(TraceExtension::Trace);
118   } else if (name->Equals(String::New("js_trace"))) {
119     return v8::FunctionTemplate::New(TraceExtension::JSTrace);
120   } else if (name->Equals(String::New("js_entry_sp"))) {
121     return v8::FunctionTemplate::New(TraceExtension::JSEntrySP);
122   } else if (name->Equals(String::New("js_entry_sp_level2"))) {
123     return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2);
124   } else {
125     CHECK(false);
126     return v8::Handle<v8::FunctionTemplate>();
127   }
128 }
129
130
131 Address TraceExtension::GetFP(const v8::FunctionCallbackInfo<v8::Value>& args) {
132   // Convert frame pointer from encoding as smis in the arguments to a pointer.
133   CHECK_EQ(2, args.Length());  // Ignore second argument on 32-bit platform.
134 #if defined(V8_HOST_ARCH_32_BIT)
135   Address fp = *reinterpret_cast<Address*>(*args[0]);
136 #elif defined(V8_HOST_ARCH_64_BIT)
137   int64_t low_bits = *reinterpret_cast<uint64_t*>(*args[0]) >> 32;
138   int64_t high_bits = *reinterpret_cast<uint64_t*>(*args[1]);
139   Address fp = reinterpret_cast<Address>(high_bits | low_bits);
140 #else
141 #error Host architecture is neither 32-bit nor 64-bit.
142 #endif
143   printf("Trace: %p\n", fp);
144   return fp;
145 }
146
147
148 void TraceExtension::Trace(const v8::FunctionCallbackInfo<v8::Value>& args) {
149   DoTrace(GetFP(args));
150 }
151
152
153 void TraceExtension::JSTrace(const v8::FunctionCallbackInfo<v8::Value>& args) {
154   DoTraceHideCEntryFPAddress(GetFP(args));
155 }
156
157
158 static Address GetJsEntrySp() {
159   CHECK_NE(NULL, CcTest::i_isolate()->thread_local_top());
160   return CcTest::i_isolate()->js_entry_sp();
161 }
162
163
164 void TraceExtension::JSEntrySP(
165     const v8::FunctionCallbackInfo<v8::Value>& args) {
166   CHECK_NE(0, GetJsEntrySp());
167 }
168
169
170 void TraceExtension::JSEntrySPLevel2(
171     const v8::FunctionCallbackInfo<v8::Value>& args) {
172   v8::HandleScope scope(args.GetIsolate());
173   const Address js_entry_sp = GetJsEntrySp();
174   CHECK_NE(0, js_entry_sp);
175   CompileRun("js_entry_sp();");
176   CHECK_EQ(js_entry_sp, GetJsEntrySp());
177 }
178
179
180 static TraceExtension kTraceExtension;
181 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
182
183
184 static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) {
185   i::Code* code = function->code();
186   return code->contains(addr);
187 }
188
189
190 static bool IsAddressWithinFuncCode(v8::Local<v8::Context> context,
191                                     const char* func_name,
192                                     Address addr) {
193   v8::Local<v8::Value> func = context->Global()->Get(v8_str(func_name));
194   CHECK(func->IsFunction());
195   JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func));
196   return IsAddressWithinFuncCode(js_func, addr);
197 }
198
199
200 // This C++ function is called as a constructor, to grab the frame pointer
201 // from the calling function.  When this function runs, the stack contains
202 // a C_Entry frame and a Construct frame above the calling function's frame.
203 static void construct_call(const v8::FunctionCallbackInfo<v8::Value>& args) {
204   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
205   i::StackFrameIterator frame_iterator(isolate);
206   CHECK(frame_iterator.frame()->is_exit());
207   frame_iterator.Advance();
208   CHECK(frame_iterator.frame()->is_construct());
209   frame_iterator.Advance();
210   i::StackFrame* calling_frame = frame_iterator.frame();
211   CHECK(calling_frame->is_java_script());
212
213 #if defined(V8_HOST_ARCH_32_BIT)
214   int32_t low_bits = reinterpret_cast<int32_t>(calling_frame->fp());
215   args.This()->Set(v8_str("low_bits"), v8_num(low_bits >> 1));
216 #elif defined(V8_HOST_ARCH_64_BIT)
217   uint64_t fp = reinterpret_cast<uint64_t>(calling_frame->fp());
218   int32_t low_bits = static_cast<int32_t>(fp & 0xffffffff);
219   int32_t high_bits = static_cast<int32_t>(fp >> 32);
220   args.This()->Set(v8_str("low_bits"), v8_num(low_bits));
221   args.This()->Set(v8_str("high_bits"), v8_num(high_bits));
222 #else
223 #error Host architecture is neither 32-bit nor 64-bit.
224 #endif
225   args.GetReturnValue().Set(args.This());
226 }
227
228
229 // Use the API to create a JSFunction object that calls the above C++ function.
230 void CreateFramePointerGrabberConstructor(v8::Local<v8::Context> context,
231                                           const char* constructor_name) {
232     Local<v8::FunctionTemplate> constructor_template =
233         v8::FunctionTemplate::New(construct_call);
234     constructor_template->SetClassName(v8_str("FPGrabber"));
235     Local<Function> fun = constructor_template->GetFunction();
236     context->Global()->Set(v8_str(constructor_name), fun);
237 }
238
239
240 // Creates a global function named 'func_name' that calls the tracing
241 // function 'trace_func_name' with an actual EBP register value,
242 // encoded as one or two Smis.
243 static void CreateTraceCallerFunction(v8::Local<v8::Context> context,
244                                       const char* func_name,
245                                       const char* trace_func_name) {
246   i::EmbeddedVector<char, 256> trace_call_buf;
247   i::OS::SNPrintF(trace_call_buf,
248                   "function %s() {"
249                   "  fp = new FPGrabber();"
250                   "  %s(fp.low_bits, fp.high_bits);"
251                   "}",
252                   func_name, trace_func_name);
253
254   // Create the FPGrabber function, which grabs the caller's frame pointer
255   // when called as a constructor.
256   CreateFramePointerGrabberConstructor(context, "FPGrabber");
257
258   // Compile the script.
259   CompileRun(trace_call_buf.start());
260 }
261
262
263 // This test verifies that stack tracing works when called during
264 // execution of a native function called from JS code. In this case,
265 // TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack
266 // walking.
267 TEST(CFromJSStackTrace) {
268   // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test.
269   i::FLAG_use_inlining = false;
270
271   TickSample sample;
272   InitTraceEnv(&sample);
273
274   v8::HandleScope scope(CcTest::isolate());
275   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
276   v8::Context::Scope context_scope(context);
277
278   // Create global function JSFuncDoTrace which calls
279   // extension function trace() with the current frame pointer value.
280   CreateTraceCallerFunction(context, "JSFuncDoTrace", "trace");
281   Local<Value> result = CompileRun(
282       "function JSTrace() {"
283       "         JSFuncDoTrace();"
284       "};\n"
285       "JSTrace();\n"
286       "true;");
287   CHECK(!result.IsEmpty());
288   // When stack tracer is invoked, the stack should look as follows:
289   // script [JS]
290   //   JSTrace() [JS]
291   //     JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
292   //       trace(EBP) [native (extension)]
293   //         DoTrace(EBP) [native]
294   //           TickSample::Trace
295
296   CHECK(sample.has_external_callback);
297   CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback);
298
299   // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
300   int base = 0;
301   CHECK_GT(sample.frames_count, base + 1);
302
303   CHECK(IsAddressWithinFuncCode(
304       context, "JSFuncDoTrace", sample.stack[base + 0]));
305   CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 1]));
306 }
307
308
309 // This test verifies that stack tracing works when called during
310 // execution of JS code. However, as calling TickSample::Trace requires
311 // entering native code, we can only emulate pure JS by erasing
312 // Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame
313 // pointer value as a starting point for stack walking.
314 TEST(PureJSStackTrace) {
315   // This test does not pass with inlining enabled since inlined functions
316   // don't appear in the stack trace.
317   i::FLAG_use_inlining = false;
318
319   TickSample sample;
320   InitTraceEnv(&sample);
321
322   v8::HandleScope scope(CcTest::isolate());
323   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
324   v8::Context::Scope context_scope(context);
325
326   // Create global function JSFuncDoTrace which calls
327   // extension function js_trace() with the current frame pointer value.
328   CreateTraceCallerFunction(context, "JSFuncDoTrace", "js_trace");
329   Local<Value> result = CompileRun(
330       "function JSTrace() {"
331       "         JSFuncDoTrace();"
332       "};\n"
333       "function OuterJSTrace() {"
334       "         JSTrace();"
335       "};\n"
336       "OuterJSTrace();\n"
337       "true;");
338   CHECK(!result.IsEmpty());
339   // When stack tracer is invoked, the stack should look as follows:
340   // script [JS]
341   //   OuterJSTrace() [JS]
342   //     JSTrace() [JS]
343   //       JSFuncDoTrace() [JS]
344   //         js_trace(EBP) [native (extension)]
345   //           DoTraceHideCEntryFPAddress(EBP) [native]
346   //             TickSample::Trace
347   //
348
349   CHECK(sample.has_external_callback);
350   CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback);
351
352   // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
353   int base = 0;
354   CHECK_GT(sample.frames_count, base + 1);
355   CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 0]));
356   CHECK(IsAddressWithinFuncCode(
357       context, "OuterJSTrace", sample.stack[base + 1]));
358 }
359
360
361 static void CFuncDoTrace(byte dummy_parameter) {
362   Address fp;
363 #ifdef __GNUC__
364   fp = reinterpret_cast<Address>(__builtin_frame_address(0));
365 #elif defined _MSC_VER
366   // Approximate a frame pointer address. We compile without base pointers,
367   // so we can't trust ebp/rbp.
368   fp = &dummy_parameter - 2 * sizeof(void*);  // NOLINT
369 #else
370 #error Unexpected platform.
371 #endif
372   DoTrace(fp);
373 }
374
375
376 static int CFunc(int depth) {
377   if (depth <= 0) {
378     CFuncDoTrace(0);
379     return 0;
380   } else {
381     return CFunc(depth - 1) + 1;
382   }
383 }
384
385
386 // This test verifies that stack tracing doesn't crash when called on
387 // pure native code. TickSample::Trace only unrolls JS code, so we can't
388 // get any meaningful info here.
389 TEST(PureCStackTrace) {
390   TickSample sample;
391   InitTraceEnv(&sample);
392   v8::HandleScope scope(CcTest::isolate());
393   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
394   v8::Context::Scope context_scope(context);
395   // Check that sampler doesn't crash
396   CHECK_EQ(10, CFunc(10));
397 }
398
399
400 TEST(JsEntrySp) {
401   v8::HandleScope scope(CcTest::isolate());
402   v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION);
403   v8::Context::Scope context_scope(context);
404   CHECK_EQ(0, GetJsEntrySp());
405   CompileRun("a = 1; b = a + 1;");
406   CHECK_EQ(0, GetJsEntrySp());
407   CompileRun("js_entry_sp();");
408   CHECK_EQ(0, GetJsEntrySp());
409   CompileRun("js_entry_sp_level2();");
410   CHECK_EQ(0, GetJsEntrySp());
411 }