| 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 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 55 // pure JS code is being executed | 55 // pure JS code is being executed |
| 56 static void DoTraceHideCEntryFPAddress(unsigned int fp) { | 56 static void DoTraceHideCEntryFPAddress(unsigned int fp) { |
| 57 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); | 57 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |
| 58 CHECK(saved_c_frame_fp); | 58 CHECK(saved_c_frame_fp); |
| 59 *(Top::c_entry_fp_address()) = 0; | 59 *(Top::c_entry_fp_address()) = 0; |
| 60 DoTrace(fp); | 60 DoTrace(fp); |
| 61 *(Top::c_entry_fp_address()) = saved_c_frame_fp; | 61 *(Top::c_entry_fp_address()) = saved_c_frame_fp; |
| 62 } | 62 } |
| 63 | 63 |
| 64 | 64 |
| 65 static void CheckRetAddrIsInFunction(const char* func_name, | |
| 66 unsigned int ret_addr, | |
| 67 unsigned int func_start_addr, | |
| 68 unsigned int func_len) { | |
| 69 printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\n", | |
| 70 func_name, func_start_addr, ret_addr, func_start_addr + func_len); | |
| 71 CHECK_GE(ret_addr, func_start_addr); | |
| 72 CHECK_GE(func_start_addr + func_len, ret_addr); | |
| 73 } | |
| 74 | |
| 75 | |
| 76 static void CheckRetAddrIsInJSFunction(const char* func_name, | |
| 77 unsigned int ret_addr, | |
| 78 Handle<JSFunction> func) { | |
| 79 v8::internal::Code* func_code = func->code(); | |
| 80 CheckRetAddrIsInFunction( | |
| 81 func_name, ret_addr, | |
| 82 reinterpret_cast<unsigned int>(func_code->instruction_start()), | |
| 83 func_code->ExecutableSize()); | |
| 84 } | |
| 85 | |
| 86 | |
| 87 // --- T r a c e E x t e n s i o n --- | 65 // --- T r a c e E x t e n s i o n --- |
| 88 | 66 |
| 89 class TraceExtension : public v8::Extension { | 67 class TraceExtension : public v8::Extension { |
| 90 public: | 68 public: |
| 91 TraceExtension() : v8::Extension("v8/trace", kSource) { } | 69 TraceExtension() : v8::Extension("v8/trace", kSource) { } |
| 92 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | 70 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
| 93 v8::Handle<String> name); | 71 v8::Handle<String> name); |
| 94 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); | 72 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); |
| 95 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); | 73 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); |
| 96 private: | 74 private: |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { | 112 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |
| 135 DoTraceHideCEntryFPAddress(GetFP(args)); | 113 DoTraceHideCEntryFPAddress(GetFP(args)); |
| 136 return v8::Undefined(); | 114 return v8::Undefined(); |
| 137 } | 115 } |
| 138 | 116 |
| 139 | 117 |
| 140 static TraceExtension kTraceExtension; | 118 static TraceExtension kTraceExtension; |
| 141 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); | 119 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |
| 142 | 120 |
| 143 | 121 |
| 144 static void InitializeVM() { | |
| 145 if (env.IsEmpty()) { | |
| 146 v8::HandleScope scope; | |
| 147 const char* extensions[] = { "v8/trace" }; | |
| 148 v8::ExtensionConfiguration config(1, extensions); | |
| 149 env = v8::Context::New(&config); | |
| 150 } | |
| 151 v8::HandleScope scope; | |
| 152 env->Enter(); | |
| 153 } | |
| 154 | |
| 155 | |
| 156 static Handle<JSFunction> CompileFunction(const char* source) { | |
| 157 return v8::Utils::OpenHandle(*Script::Compile(String::New(source))); | |
| 158 } | |
| 159 | |
| 160 | |
| 161 static void CompileRun(const char* source) { | |
| 162 Script::Compile(String::New(source))->Run(); | |
| 163 } | |
| 164 | |
| 165 | |
| 166 static Local<Value> GetGlobalProperty(const char* name) { | |
| 167 return env->Global()->Get(String::New(name)); | |
| 168 } | |
| 169 | |
| 170 | |
| 171 static Handle<JSFunction> GetGlobalJSFunction(const char* name) { | |
| 172 Handle<JSFunction> js_func(JSFunction::cast( | |
| 173 *(v8::Utils::OpenHandle( | |
| 174 *GetGlobalProperty(name))))); | |
| 175 return js_func; | |
| 176 } | |
| 177 | |
| 178 | |
| 179 static void CheckRetAddrIsInJSFunction(const char* func_name, | |
| 180 unsigned int ret_addr) { | |
| 181 CheckRetAddrIsInJSFunction(func_name, ret_addr, | |
| 182 GetGlobalJSFunction(func_name)); | |
| 183 } | |
| 184 | |
| 185 | |
| 186 static void SetGlobalProperty(const char* name, Local<Value> value) { | |
| 187 env->Global()->Set(String::New(name), value); | |
| 188 } | |
| 189 | |
| 190 | |
| 191 static bool Patch(byte* from, | |
| 192 size_t num, | |
| 193 byte* original, | |
| 194 byte* patch, | |
| 195 size_t patch_len) { | |
| 196 byte* to = from + num; | |
| 197 do { | |
| 198 from = static_cast<byte*>(memchr(from, *original, to - from)); | |
| 199 CHECK(from != NULL); | |
| 200 if (memcmp(original, from, patch_len) == 0) { | |
| 201 memcpy(from, patch, patch_len); | |
| 202 return true; | |
| 203 } else { | |
| 204 from++; | |
| 205 } | |
| 206 } while (to - from > 0); | |
| 207 return false; | |
| 208 } | |
| 209 | |
| 210 | |
| 211 // Creates a global function named 'func_name' that calls the tracing | |
| 212 // function 'trace_func_name' with an actual EBP register value, | |
| 213 // shifted right to be presented as Smi. | |
| 214 static void CreateTraceCallerFunction(const char* func_name, | |
| 215 const char* trace_func_name) { | |
| 216 ::v8::internal::EmbeddedVector<char, 256> trace_call_buf; | |
| 217 ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name); | |
| 218 Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); | |
| 219 CHECK(!func.is_null()); | |
| 220 v8::internal::Code* func_code = func->code(); | |
| 221 CHECK(func_code->IsCode()); | |
| 222 | |
| 223 // push 0xcccc (= 0x6666 << 1) | |
| 224 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; | |
| 225 // mov eax,ebp; shr eax; push eax; | |
| 226 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; | |
| 227 // Patch generated code to replace pushing of a constant with | |
| 228 // pushing of ebp contents in a Smi | |
| 229 CHECK(Patch(func_code->instruction_start(), | |
| 230 func_code->instruction_size(), | |
| 231 original, patch, sizeof(patch))); | |
| 232 | |
| 233 SetGlobalProperty(func_name, v8::ToApi<Value>(func)); | |
| 234 } | |
| 235 | |
| 236 | |
| 237 TEST(CFromJSStackTrace) { | |
| 238 TickSample sample; | |
| 239 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | |
| 240 InitTraceEnv(&tracer, &sample); | |
| 241 | |
| 242 InitializeVM(); | |
| 243 v8::HandleScope scope; | |
| 244 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); | |
| 245 CompileRun( | |
| 246 "function JSTrace() {" | |
| 247 " JSFuncDoTrace();" | |
| 248 "};\n" | |
| 249 "JSTrace();"); | |
| 250 CHECK_GT(sample.frames_count, 1); | |
| 251 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace" | |
| 252 CheckRetAddrIsInJSFunction("JSFuncDoTrace", | |
| 253 reinterpret_cast<unsigned int>(sample.stack[0])); | |
| 254 CheckRetAddrIsInJSFunction("JSTrace", | |
| 255 reinterpret_cast<unsigned int>(sample.stack[1])); | |
| 256 } | |
| 257 | |
| 258 | |
| 259 TEST(PureJSStackTrace) { | |
| 260 TickSample sample; | |
| 261 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | |
| 262 InitTraceEnv(&tracer, &sample); | |
| 263 | |
| 264 InitializeVM(); | |
| 265 v8::HandleScope scope; | |
| 266 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); | |
| 267 CompileRun( | |
| 268 "function JSTrace() {" | |
| 269 " JSFuncDoTrace();" | |
| 270 "};\n" | |
| 271 "function OuterJSTrace() {" | |
| 272 " JSTrace();" | |
| 273 "};\n" | |
| 274 "OuterJSTrace();"); | |
| 275 CHECK_GT(sample.frames_count, 1); | |
| 276 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" | |
| 277 CheckRetAddrIsInJSFunction("JSTrace", | |
| 278 reinterpret_cast<unsigned int>(sample.stack[0])); | |
| 279 CheckRetAddrIsInJSFunction("OuterJSTrace", | |
| 280 reinterpret_cast<unsigned int>(sample.stack[1])); | |
| 281 } | |
| 282 | |
| 283 | |
| 284 static void CFuncDoTrace() { | 122 static void CFuncDoTrace() { |
| 285 unsigned int fp; | 123 unsigned int fp; |
| 286 #ifdef __GNUC__ | 124 #ifdef __GNUC__ |
| 287 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); | 125 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); |
| 288 #elif defined _MSC_VER | 126 #elif defined _MSC_VER |
| 289 __asm mov [fp], ebp // NOLINT | 127 __asm mov [fp], ebp // NOLINT |
| 290 #endif | 128 #endif |
| 291 DoTrace(fp); | 129 DoTrace(fp); |
| 292 } | 130 } |
| 293 | 131 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 305 TEST(PureCStackTrace) { | 143 TEST(PureCStackTrace) { |
| 306 TickSample sample; | 144 TickSample sample; |
| 307 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | 145 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
| 308 InitTraceEnv(&tracer, &sample); | 146 InitTraceEnv(&tracer, &sample); |
| 309 // Check that sampler doesn't crash | 147 // Check that sampler doesn't crash |
| 310 CHECK_EQ(10, CFunc(10)); | 148 CHECK_EQ(10, CFunc(10)); |
| 311 } | 149 } |
| 312 | 150 |
| 313 | 151 |
| 314 #endif // ENABLE_LOGGING_AND_PROFILING | 152 #endif // ENABLE_LOGGING_AND_PROFILING |
| OLD | NEW |