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