| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright 2006-2009 the V8 project authors. All rights reserved. |  | 
|    2 // |  | 
|    3 // Tests of profiler-related functions from log.h |  | 
|    4  |  | 
|    5 #ifdef ENABLE_LOGGING_AND_PROFILING |  | 
|    6  |  | 
|    7 #include <stdlib.h> |  | 
|    8  |  | 
|    9 #include "v8.h" |  | 
|   10  |  | 
|   11 #include "codegen.h" |  | 
|   12 #include "log.h" |  | 
|   13 #include "top.h" |  | 
|   14 #include "cctest.h" |  | 
|   15 #include "disassembler.h" |  | 
|   16 #include "register-allocator-inl.h" |  | 
|   17  |  | 
|   18 using v8::Function; |  | 
|   19 using v8::Local; |  | 
|   20 using v8::Object; |  | 
|   21 using v8::Script; |  | 
|   22 using v8::String; |  | 
|   23 using v8::Value; |  | 
|   24  |  | 
|   25 using v8::internal::byte; |  | 
|   26 using v8::internal::Address; |  | 
|   27 using v8::internal::Handle; |  | 
|   28 using v8::internal::JSFunction; |  | 
|   29 using v8::internal::StackTracer; |  | 
|   30 using v8::internal::TickSample; |  | 
|   31 using v8::internal::Top; |  | 
|   32  |  | 
|   33 namespace i = v8::internal; |  | 
|   34  |  | 
|   35  |  | 
|   36 static v8::Persistent<v8::Context> env; |  | 
|   37  |  | 
|   38  |  | 
|   39 static struct { |  | 
|   40   TickSample* sample; |  | 
|   41 } trace_env = { NULL }; |  | 
|   42  |  | 
|   43  |  | 
|   44 static void InitTraceEnv(TickSample* sample) { |  | 
|   45   trace_env.sample = sample; |  | 
|   46 } |  | 
|   47  |  | 
|   48  |  | 
|   49 static void DoTrace(Address fp) { |  | 
|   50   trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp); |  | 
|   51   // sp is only used to define stack high bound |  | 
|   52   trace_env.sample->sp = |  | 
|   53       reinterpret_cast<unsigned int>(trace_env.sample) - 10240; |  | 
|   54   StackTracer::Trace(trace_env.sample); |  | 
|   55 } |  | 
|   56  |  | 
|   57  |  | 
|   58 // Hide c_entry_fp to emulate situation when sampling is done while |  | 
|   59 // pure JS code is being executed |  | 
|   60 static void DoTraceHideCEntryFPAddress(Address fp) { |  | 
|   61   v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |  | 
|   62   CHECK(saved_c_frame_fp); |  | 
|   63   *(Top::c_entry_fp_address()) = 0; |  | 
|   64   DoTrace(fp); |  | 
|   65   *(Top::c_entry_fp_address()) = saved_c_frame_fp; |  | 
|   66 } |  | 
|   67  |  | 
|   68  |  | 
|   69 static void CheckRetAddrIsInFunction(const char* func_name, |  | 
|   70                                      Address ret_addr, |  | 
|   71                                      Address func_start_addr, |  | 
|   72                                      unsigned int func_len) { |  | 
|   73   printf("CheckRetAddrIsInFunction \"%s\": %p %p %p\n", |  | 
|   74          func_name, func_start_addr, ret_addr, func_start_addr + func_len); |  | 
|   75   CHECK_GE(ret_addr, func_start_addr); |  | 
|   76   CHECK_GE(func_start_addr + func_len, ret_addr); |  | 
|   77 } |  | 
|   78  |  | 
|   79  |  | 
|   80 static void CheckRetAddrIsInJSFunction(const char* func_name, |  | 
|   81                                        Address ret_addr, |  | 
|   82                                        Handle<JSFunction> func) { |  | 
|   83   v8::internal::Code* func_code = func->code(); |  | 
|   84   CheckRetAddrIsInFunction( |  | 
|   85       func_name, ret_addr, |  | 
|   86       func_code->instruction_start(), |  | 
|   87       func_code->ExecutableSize()); |  | 
|   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 v8::Handle<v8::Value> Trace(const v8::Arguments& args); |  | 
|   99   static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); |  | 
|  100   static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args); |  | 
|  101   static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args); |  | 
|  102  private: |  | 
|  103   static Address GetFP(const v8::Arguments& 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::Arguments& args) { |  | 
|  132   CHECK_EQ(1, args.Length()); |  | 
|  133   Address fp = reinterpret_cast<Address>(args[0]->Int32Value() << 2); |  | 
|  134   printf("Trace: %p\n", fp); |  | 
|  135   return fp; |  | 
|  136 } |  | 
|  137  |  | 
|  138  |  | 
|  139 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |  | 
|  140   DoTrace(GetFP(args)); |  | 
|  141   return v8::Undefined(); |  | 
|  142 } |  | 
|  143  |  | 
|  144  |  | 
|  145 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |  | 
|  146   DoTraceHideCEntryFPAddress(GetFP(args)); |  | 
|  147   return v8::Undefined(); |  | 
|  148 } |  | 
|  149  |  | 
|  150  |  | 
|  151 static Address GetJsEntrySp() { |  | 
|  152   CHECK_NE(NULL, Top::GetCurrentThread()); |  | 
|  153   return Top::js_entry_sp(Top::GetCurrentThread()); |  | 
|  154 } |  | 
|  155  |  | 
|  156  |  | 
|  157 v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) { |  | 
|  158   CHECK_NE(0, GetJsEntrySp()); |  | 
|  159   return v8::Undefined(); |  | 
|  160 } |  | 
|  161  |  | 
|  162  |  | 
|  163 static void CompileRun(const char* source) { |  | 
|  164   Script::Compile(String::New(source))->Run(); |  | 
|  165 } |  | 
|  166  |  | 
|  167  |  | 
|  168 v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2( |  | 
|  169     const v8::Arguments& args) { |  | 
|  170   v8::HandleScope scope; |  | 
|  171   const Address js_entry_sp = GetJsEntrySp(); |  | 
|  172   CHECK_NE(0, js_entry_sp); |  | 
|  173   CompileRun("js_entry_sp();"); |  | 
|  174   CHECK_EQ(js_entry_sp, GetJsEntrySp()); |  | 
|  175   return v8::Undefined(); |  | 
|  176 } |  | 
|  177  |  | 
|  178  |  | 
|  179 static TraceExtension kTraceExtension; |  | 
|  180 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |  | 
|  181  |  | 
|  182  |  | 
|  183 static void InitializeVM() { |  | 
|  184   if (env.IsEmpty()) { |  | 
|  185     v8::HandleScope scope; |  | 
|  186     const char* extensions[] = { "v8/trace" }; |  | 
|  187     v8::ExtensionConfiguration config(1, extensions); |  | 
|  188     env = v8::Context::New(&config); |  | 
|  189   } |  | 
|  190   v8::HandleScope scope; |  | 
|  191   env->Enter(); |  | 
|  192 } |  | 
|  193  |  | 
|  194  |  | 
|  195 static Handle<JSFunction> CompileFunction(const char* source) { |  | 
|  196   return v8::Utils::OpenHandle(*Script::Compile(String::New(source))); |  | 
|  197 } |  | 
|  198  |  | 
|  199  |  | 
|  200 static Local<Value> GetGlobalProperty(const char* name) { |  | 
|  201   return env->Global()->Get(String::New(name)); |  | 
|  202 } |  | 
|  203  |  | 
|  204  |  | 
|  205 static Handle<JSFunction> GetGlobalJSFunction(const char* name) { |  | 
|  206   Handle<JSFunction> js_func(JSFunction::cast( |  | 
|  207                                  *(v8::Utils::OpenHandle( |  | 
|  208                                        *GetGlobalProperty(name))))); |  | 
|  209   return js_func; |  | 
|  210 } |  | 
|  211  |  | 
|  212  |  | 
|  213 static void CheckRetAddrIsInJSFunction(const char* func_name, |  | 
|  214                                        Address ret_addr) { |  | 
|  215   CheckRetAddrIsInJSFunction(func_name, ret_addr, |  | 
|  216                              GetGlobalJSFunction(func_name)); |  | 
|  217 } |  | 
|  218  |  | 
|  219  |  | 
|  220 static void SetGlobalProperty(const char* name, Local<Value> value) { |  | 
|  221   env->Global()->Set(String::New(name), value); |  | 
|  222 } |  | 
|  223  |  | 
|  224  |  | 
|  225 static Handle<v8::internal::String> NewString(const char* s) { |  | 
|  226   return i::Factory::NewStringFromAscii(i::CStrVector(s)); |  | 
|  227 } |  | 
|  228  |  | 
|  229  |  | 
|  230 namespace v8 { |  | 
|  231 namespace internal { |  | 
|  232  |  | 
|  233 class CodeGeneratorPatcher { |  | 
|  234  public: |  | 
|  235   CodeGeneratorPatcher() { |  | 
|  236     CodeGenerator::InlineRuntimeLUT genGetFramePointer = |  | 
|  237         {&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"}; |  | 
|  238     // _FastCharCodeAt is not used in our tests. |  | 
|  239     bool result = CodeGenerator::PatchInlineRuntimeEntry( |  | 
|  240         NewString("_FastCharCodeAt"), |  | 
|  241         genGetFramePointer, &oldInlineEntry); |  | 
|  242     CHECK(result); |  | 
|  243   } |  | 
|  244  |  | 
|  245   ~CodeGeneratorPatcher() { |  | 
|  246     CHECK(CodeGenerator::PatchInlineRuntimeEntry( |  | 
|  247         NewString("_GetFramePointer"), |  | 
|  248         oldInlineEntry, NULL)); |  | 
|  249   } |  | 
|  250  |  | 
|  251  private: |  | 
|  252   CodeGenerator::InlineRuntimeLUT oldInlineEntry; |  | 
|  253 }; |  | 
|  254  |  | 
|  255 } }  // namespace v8::internal |  | 
|  256  |  | 
|  257  |  | 
|  258 // Creates a global function named 'func_name' that calls the tracing |  | 
|  259 // function 'trace_func_name' with an actual EBP register value, |  | 
|  260 // shifted right to be presented as Smi. |  | 
|  261 static void CreateTraceCallerFunction(const char* func_name, |  | 
|  262                                       const char* trace_func_name) { |  | 
|  263   i::EmbeddedVector<char, 256> trace_call_buf; |  | 
|  264   i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name); |  | 
|  265  |  | 
|  266   // Compile the script. |  | 
|  267   i::CodeGeneratorPatcher patcher; |  | 
|  268   bool allow_natives_syntax = i::FLAG_allow_natives_syntax; |  | 
|  269   i::FLAG_allow_natives_syntax = true; |  | 
|  270   Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |  | 
|  271   CHECK(!func.is_null()); |  | 
|  272   i::FLAG_allow_natives_syntax = allow_natives_syntax; |  | 
|  273  |  | 
|  274 #ifdef DEBUG |  | 
|  275   v8::internal::Code* func_code = func->code(); |  | 
|  276   CHECK(func_code->IsCode()); |  | 
|  277   func_code->Print(); |  | 
|  278 #endif |  | 
|  279  |  | 
|  280   SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |  | 
|  281 } |  | 
|  282  |  | 
|  283  |  | 
|  284 TEST(CFromJSStackTrace) { |  | 
|  285   TickSample sample; |  | 
|  286   InitTraceEnv(&sample); |  | 
|  287  |  | 
|  288   InitializeVM(); |  | 
|  289   v8::HandleScope scope; |  | 
|  290   CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |  | 
|  291   CompileRun( |  | 
|  292       "function JSTrace() {" |  | 
|  293       "         JSFuncDoTrace();" |  | 
|  294       "};\n" |  | 
|  295       "JSTrace();"); |  | 
|  296   CHECK_GT(sample.frames_count, 1); |  | 
|  297   // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace" |  | 
|  298   CheckRetAddrIsInJSFunction("JSFuncDoTrace", |  | 
|  299                              sample.stack[0]); |  | 
|  300   CheckRetAddrIsInJSFunction("JSTrace", |  | 
|  301                              sample.stack[1]); |  | 
|  302 } |  | 
|  303  |  | 
|  304  |  | 
|  305 TEST(PureJSStackTrace) { |  | 
|  306   TickSample sample; |  | 
|  307   InitTraceEnv(&sample); |  | 
|  308  |  | 
|  309   InitializeVM(); |  | 
|  310   v8::HandleScope scope; |  | 
|  311   CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); |  | 
|  312   CompileRun( |  | 
|  313       "function JSTrace() {" |  | 
|  314       "         JSFuncDoTrace();" |  | 
|  315       "};\n" |  | 
|  316       "function OuterJSTrace() {" |  | 
|  317       "         JSTrace();" |  | 
|  318       "};\n" |  | 
|  319       "OuterJSTrace();"); |  | 
|  320   CHECK_GT(sample.frames_count, 1); |  | 
|  321   // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" |  | 
|  322   CheckRetAddrIsInJSFunction("JSTrace", |  | 
|  323                              sample.stack[0]); |  | 
|  324   CheckRetAddrIsInJSFunction("OuterJSTrace", |  | 
|  325                              sample.stack[1]); |  | 
|  326 } |  | 
|  327  |  | 
|  328  |  | 
|  329 static void CFuncDoTrace() { |  | 
|  330   Address fp; |  | 
|  331 #ifdef __GNUC__ |  | 
|  332   fp = reinterpret_cast<Address>(__builtin_frame_address(0)); |  | 
|  333 #elif defined _MSC_VER |  | 
|  334   __asm mov [fp], ebp  // NOLINT |  | 
|  335 #endif |  | 
|  336   DoTrace(fp); |  | 
|  337 } |  | 
|  338  |  | 
|  339  |  | 
|  340 static int CFunc(int depth) { |  | 
|  341   if (depth <= 0) { |  | 
|  342     CFuncDoTrace(); |  | 
|  343     return 0; |  | 
|  344   } else { |  | 
|  345     return CFunc(depth - 1) + 1; |  | 
|  346   } |  | 
|  347 } |  | 
|  348  |  | 
|  349  |  | 
|  350 TEST(PureCStackTrace) { |  | 
|  351   TickSample sample; |  | 
|  352   InitTraceEnv(&sample); |  | 
|  353   // Check that sampler doesn't crash |  | 
|  354   CHECK_EQ(10, CFunc(10)); |  | 
|  355 } |  | 
|  356  |  | 
|  357  |  | 
|  358 TEST(JsEntrySp) { |  | 
|  359   InitializeVM(); |  | 
|  360   v8::HandleScope scope; |  | 
|  361   CHECK_EQ(0, GetJsEntrySp()); |  | 
|  362   CompileRun("a = 1; b = a + 1;"); |  | 
|  363   CHECK_EQ(0, GetJsEntrySp()); |  | 
|  364   CompileRun("js_entry_sp();"); |  | 
|  365   CHECK_EQ(0, GetJsEntrySp()); |  | 
|  366   CompileRun("js_entry_sp_level2();"); |  | 
|  367   CHECK_EQ(0, GetJsEntrySp()); |  | 
|  368 } |  | 
|  369  |  | 
|  370 #endif  // ENABLE_LOGGING_AND_PROFILING |  | 
| OLD | NEW |