PageRenderTime 112ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/test/cctest/test-log-stack-tracer.cc

https://github.com/schiznik/android_external_v8
C++ | 399 lines | 252 code | 65 blank | 82 comment | 11 complexity | 2f464ef4003071d6231c3008efdc2ca5 MD5 | raw file
  1. // 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.
  27. //
  28. // Tests of profiler-related functions from log.h
  29. #ifdef ENABLE_LOGGING_AND_PROFILING
  30. #include <stdlib.h>
  31. #include "v8.h"
  32. #include "codegen.h"
  33. #include "log.h"
  34. #include "top.h"
  35. #include "cctest.h"
  36. #include "disassembler.h"
  37. #include "register-allocator-inl.h"
  38. using v8::Function;
  39. using v8::Local;
  40. using v8::Object;
  41. using v8::Script;
  42. using v8::String;
  43. using v8::Value;
  44. using v8::internal::byte;
  45. using v8::internal::Address;
  46. using v8::internal::Handle;
  47. using v8::internal::JSFunction;
  48. using v8::internal::StackTracer;
  49. using v8::internal::TickSample;
  50. using v8::internal::Top;
  51. namespace i = v8::internal;
  52. static v8::Persistent<v8::Context> env;
  53. static struct {
  54. TickSample* sample;
  55. } trace_env = { NULL };
  56. static void InitTraceEnv(TickSample* sample) {
  57. trace_env.sample = sample;
  58. }
  59. static void DoTrace(Address fp) {
  60. trace_env.sample->fp = fp;
  61. // sp is only used to define stack high bound
  62. trace_env.sample->sp =
  63. reinterpret_cast<Address>(trace_env.sample) - 10240;
  64. StackTracer::Trace(trace_env.sample);
  65. }
  66. // Hide c_entry_fp to emulate situation when sampling is done while
  67. // pure JS code is being executed
  68. static void DoTraceHideCEntryFPAddress(Address fp) {
  69. v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
  70. CHECK(saved_c_frame_fp);
  71. *(Top::c_entry_fp_address()) = 0;
  72. DoTrace(fp);
  73. *(Top::c_entry_fp_address()) = saved_c_frame_fp;
  74. }
  75. // --- T r a c e E x t e n s i o n ---
  76. class TraceExtension : public v8::Extension {
  77. public:
  78. TraceExtension() : v8::Extension("v8/trace", kSource) { }
  79. virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
  80. v8::Handle<String> name);
  81. static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
  82. static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
  83. static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args);
  84. static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args);
  85. private:
  86. static Address GetFP(const v8::Arguments& args);
  87. static const char* kSource;
  88. };
  89. const char* TraceExtension::kSource =
  90. "native function trace();"
  91. "native function js_trace();"
  92. "native function js_entry_sp();"
  93. "native function js_entry_sp_level2();";
  94. v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
  95. v8::Handle<String> name) {
  96. if (name->Equals(String::New("trace"))) {
  97. return v8::FunctionTemplate::New(TraceExtension::Trace);
  98. } else if (name->Equals(String::New("js_trace"))) {
  99. return v8::FunctionTemplate::New(TraceExtension::JSTrace);
  100. } else if (name->Equals(String::New("js_entry_sp"))) {
  101. return v8::FunctionTemplate::New(TraceExtension::JSEntrySP);
  102. } else if (name->Equals(String::New("js_entry_sp_level2"))) {
  103. return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2);
  104. } else {
  105. CHECK(false);
  106. return v8::Handle<v8::FunctionTemplate>();
  107. }
  108. }
  109. Address TraceExtension::GetFP(const v8::Arguments& args) {
  110. // Convert frame pointer from encoding as smis in the arguments to a pointer.
  111. CHECK_EQ(2, args.Length()); // Ignore second argument on 32-bit platform.
  112. #if defined(V8_HOST_ARCH_32_BIT)
  113. Address fp = *reinterpret_cast<Address*>(*args[0]);
  114. #elif defined(V8_HOST_ARCH_64_BIT)
  115. int64_t low_bits = *reinterpret_cast<uint64_t*>(*args[0]) >> 32;
  116. int64_t high_bits = *reinterpret_cast<uint64_t*>(*args[1]);
  117. Address fp = reinterpret_cast<Address>(high_bits | low_bits);
  118. #else
  119. #error Host architecture is neither 32-bit nor 64-bit.
  120. #endif
  121. printf("Trace: %p\n", fp);
  122. return fp;
  123. }
  124. v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
  125. DoTrace(GetFP(args));
  126. return v8::Undefined();
  127. }
  128. v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
  129. DoTraceHideCEntryFPAddress(GetFP(args));
  130. return v8::Undefined();
  131. }
  132. static Address GetJsEntrySp() {
  133. CHECK_NE(NULL, Top::GetCurrentThread());
  134. return Top::js_entry_sp(Top::GetCurrentThread());
  135. }
  136. v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) {
  137. CHECK_NE(0, GetJsEntrySp());
  138. return v8::Undefined();
  139. }
  140. v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2(
  141. const v8::Arguments& args) {
  142. v8::HandleScope scope;
  143. const Address js_entry_sp = GetJsEntrySp();
  144. CHECK_NE(0, js_entry_sp);
  145. CompileRun("js_entry_sp();");
  146. CHECK_EQ(js_entry_sp, GetJsEntrySp());
  147. return v8::Undefined();
  148. }
  149. static TraceExtension kTraceExtension;
  150. v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
  151. static void InitializeVM() {
  152. if (env.IsEmpty()) {
  153. v8::HandleScope scope;
  154. const char* extensions[] = { "v8/trace" };
  155. v8::ExtensionConfiguration config(1, extensions);
  156. env = v8::Context::New(&config);
  157. }
  158. v8::HandleScope scope;
  159. env->Enter();
  160. }
  161. static void CheckJSFunctionAtAddress(const char* func_name, Address addr) {
  162. i::Object* obj = i::HeapObject::FromAddress(addr);
  163. CHECK(obj->IsJSFunction());
  164. CHECK(JSFunction::cast(obj)->shared()->name()->IsString());
  165. i::SmartPointer<char> found_name =
  166. i::String::cast(
  167. JSFunction::cast(
  168. obj)->shared()->name())->ToCString();
  169. CHECK_EQ(func_name, *found_name);
  170. }
  171. // This C++ function is called as a constructor, to grab the frame pointer
  172. // from the calling function. When this function runs, the stack contains
  173. // a C_Entry frame and a Construct frame above the calling function's frame.
  174. static v8::Handle<Value> construct_call(const v8::Arguments& args) {
  175. i::StackFrameIterator frame_iterator;
  176. CHECK(frame_iterator.frame()->is_exit());
  177. frame_iterator.Advance();
  178. CHECK(frame_iterator.frame()->is_construct());
  179. frame_iterator.Advance();
  180. i::StackFrame* calling_frame = frame_iterator.frame();
  181. CHECK(calling_frame->is_java_script());
  182. #if defined(V8_HOST_ARCH_32_BIT)
  183. int32_t low_bits = reinterpret_cast<int32_t>(calling_frame->fp());
  184. args.This()->Set(v8_str("low_bits"), v8_num(low_bits >> 1));
  185. #elif defined(V8_HOST_ARCH_64_BIT)
  186. uint64_t fp = reinterpret_cast<uint64_t>(calling_frame->fp());
  187. int32_t low_bits = static_cast<int32_t>(fp & 0xffffffff);
  188. int32_t high_bits = static_cast<int32_t>(fp >> 32);
  189. args.This()->Set(v8_str("low_bits"), v8_num(low_bits));
  190. args.This()->Set(v8_str("high_bits"), v8_num(high_bits));
  191. #else
  192. #error Host architecture is neither 32-bit nor 64-bit.
  193. #endif
  194. return args.This();
  195. }
  196. // Use the API to create a JSFunction object that calls the above C++ function.
  197. void CreateFramePointerGrabberConstructor(const char* constructor_name) {
  198. Local<v8::FunctionTemplate> constructor_template =
  199. v8::FunctionTemplate::New(construct_call);
  200. constructor_template->SetClassName(v8_str("FPGrabber"));
  201. Local<Function> fun = constructor_template->GetFunction();
  202. env->Global()->Set(v8_str(constructor_name), fun);
  203. }
  204. // Creates a global function named 'func_name' that calls the tracing
  205. // function 'trace_func_name' with an actual EBP register value,
  206. // encoded as one or two Smis.
  207. static void CreateTraceCallerFunction(const char* func_name,
  208. const char* trace_func_name) {
  209. i::EmbeddedVector<char, 256> trace_call_buf;
  210. i::OS::SNPrintF(trace_call_buf,
  211. "function %s() {"
  212. " fp = new FPGrabber();"
  213. " %s(fp.low_bits, fp.high_bits);"
  214. "}",
  215. func_name, trace_func_name);
  216. // Create the FPGrabber function, which grabs the caller's frame pointer
  217. // when called as a constructor.
  218. CreateFramePointerGrabberConstructor("FPGrabber");
  219. // Compile the script.
  220. CompileRun(trace_call_buf.start());
  221. }
  222. // This test verifies that stack tracing works when called during
  223. // execution of a native function called from JS code. In this case,
  224. // StackTracer uses Top::c_entry_fp as a starting point for stack
  225. // walking.
  226. TEST(CFromJSStackTrace) {
  227. TickSample sample;
  228. InitTraceEnv(&sample);
  229. InitializeVM();
  230. v8::HandleScope scope;
  231. // Create global function JSFuncDoTrace which calls
  232. // extension function trace() with the current frame pointer value.
  233. CreateTraceCallerFunction("JSFuncDoTrace", "trace");
  234. Local<Value> result = CompileRun(
  235. "function JSTrace() {"
  236. " JSFuncDoTrace();"
  237. "};\n"
  238. "JSTrace();\n"
  239. "true;");
  240. CHECK(!result.IsEmpty());
  241. // When stack tracer is invoked, the stack should look as follows:
  242. // script [JS]
  243. // JSTrace() [JS]
  244. // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi]
  245. // trace(EBP) [native (extension)]
  246. // DoTrace(EBP) [native]
  247. // StackTracer::Trace
  248. CHECK_GT(sample.frames_count, 1);
  249. // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace"
  250. CheckJSFunctionAtAddress("JSFuncDoTrace", sample.stack[0]);
  251. CheckJSFunctionAtAddress("JSTrace", sample.stack[1]);
  252. }
  253. // This test verifies that stack tracing works when called during
  254. // execution of JS code. However, as calling StackTracer requires
  255. // entering native code, we can only emulate pure JS by erasing
  256. // Top::c_entry_fp value. In this case, StackTracer uses passed frame
  257. // pointer value as a starting point for stack walking.
  258. TEST(PureJSStackTrace) {
  259. TickSample sample;
  260. InitTraceEnv(&sample);
  261. InitializeVM();
  262. v8::HandleScope scope;
  263. // Create global function JSFuncDoTrace which calls
  264. // extension function js_trace() with the current frame pointer value.
  265. CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
  266. Local<Value> result = CompileRun(
  267. "function JSTrace() {"
  268. " JSFuncDoTrace();"
  269. "};\n"
  270. "function OuterJSTrace() {"
  271. " JSTrace();"
  272. "};\n"
  273. "OuterJSTrace();\n"
  274. "true;");
  275. CHECK(!result.IsEmpty());
  276. // When stack tracer is invoked, the stack should look as follows:
  277. // script [JS]
  278. // OuterJSTrace() [JS]
  279. // JSTrace() [JS]
  280. // JSFuncDoTrace() [JS]
  281. // js_trace(EBP) [native (extension)]
  282. // DoTraceHideCEntryFPAddress(EBP) [native]
  283. // StackTracer::Trace
  284. //
  285. // The last JS function called. It is only visible through
  286. // sample.function, as its return address is above captured EBP value.
  287. CheckJSFunctionAtAddress("JSFuncDoTrace", sample.function);
  288. CHECK_GT(sample.frames_count, 1);
  289. // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
  290. CheckJSFunctionAtAddress("JSTrace", sample.stack[0]);
  291. CheckJSFunctionAtAddress("OuterJSTrace", sample.stack[1]);
  292. }
  293. static void CFuncDoTrace(byte dummy_parameter) {
  294. Address fp;
  295. #ifdef __GNUC__
  296. fp = reinterpret_cast<Address>(__builtin_frame_address(0));
  297. #elif defined _MSC_VER
  298. // Approximate a frame pointer address. We compile without base pointers,
  299. // so we can't trust ebp/rbp.
  300. fp = &dummy_parameter - 2 * sizeof(void*); // NOLINT
  301. #else
  302. #error Unexpected platform.
  303. #endif
  304. DoTrace(fp);
  305. }
  306. static int CFunc(int depth) {
  307. if (depth <= 0) {
  308. CFuncDoTrace(0);
  309. return 0;
  310. } else {
  311. return CFunc(depth - 1) + 1;
  312. }
  313. }
  314. // This test verifies that stack tracing doesn't crash when called on
  315. // pure native code. StackTracer only unrolls JS code, so we can't
  316. // get any meaningful info here.
  317. TEST(PureCStackTrace) {
  318. TickSample sample;
  319. InitTraceEnv(&sample);
  320. // Check that sampler doesn't crash
  321. CHECK_EQ(10, CFunc(10));
  322. }
  323. TEST(JsEntrySp) {
  324. InitializeVM();
  325. v8::HandleScope scope;
  326. CHECK_EQ(0, GetJsEntrySp());
  327. CompileRun("a = 1; b = a + 1;");
  328. CHECK_EQ(0, GetJsEntrySp());
  329. CompileRun("js_entry_sp();");
  330. CHECK_EQ(0, GetJsEntrySp());
  331. CompileRun("js_entry_sp_level2();");
  332. CHECK_EQ(0, GetJsEntrySp());
  333. }
  334. #endif // ENABLE_LOGGING_AND_PROFILING