| 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 "log.h" | 11 #include "log.h" |
| 12 #include "top.h" |
| 12 #include "cctest.h" | 13 #include "cctest.h" |
| 13 | 14 |
| 14 using v8::Function; | 15 using v8::Function; |
| 15 using v8::Local; | 16 using v8::Local; |
| 16 using v8::Object; | 17 using v8::Object; |
| 17 using v8::Script; | 18 using v8::Script; |
| 18 using v8::String; | 19 using v8::String; |
| 19 using v8::Value; | 20 using v8::Value; |
| 20 | 21 |
| 21 using v8::internal::byte; | 22 using v8::internal::byte; |
| 22 using v8::internal::Handle; | 23 using v8::internal::Handle; |
| 23 using v8::internal::JSFunction; | 24 using v8::internal::JSFunction; |
| 24 using v8::internal::StackTracer; | 25 using v8::internal::StackTracer; |
| 25 using v8::internal::TickSample; | 26 using v8::internal::TickSample; |
| 27 using v8::internal::Top; |
| 26 | 28 |
| 27 | 29 |
| 28 static v8::Persistent<v8::Context> env; | 30 static v8::Persistent<v8::Context> env; |
| 29 | 31 |
| 30 | 32 |
| 31 static struct { | 33 static struct { |
| 32 StackTracer* tracer; | 34 StackTracer* tracer; |
| 33 TickSample* sample; | 35 TickSample* sample; |
| 34 } trace_env; | 36 } trace_env = { NULL, NULL }; |
| 35 | 37 |
| 36 | 38 |
| 37 static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { | 39 static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { |
| 38 trace_env.tracer = tracer; | 40 trace_env.tracer = tracer; |
| 39 trace_env.sample = sample; | 41 trace_env.sample = sample; |
| 40 } | 42 } |
| 41 | 43 |
| 42 | 44 |
| 43 static void DoTrace(unsigned int fp) { | 45 static void DoTrace(unsigned int fp) { |
| 44 trace_env.sample->fp = fp; | 46 trace_env.sample->fp = fp; |
| 45 // something that is less than fp | 47 // sp is only used to define stack high bound |
| 46 trace_env.sample->sp = trace_env.sample->fp - 100; | 48 trace_env.sample->sp = |
| 49 reinterpret_cast<unsigned int>(trace_env.sample) - 10240; |
| 47 trace_env.tracer->Trace(trace_env.sample); | 50 trace_env.tracer->Trace(trace_env.sample); |
| 48 } | 51 } |
| 49 | 52 |
| 50 | 53 |
| 51 static void CFuncDoTrace() { | 54 // Hide c_entry_fp to emulate situation when sampling is done while |
| 52 unsigned int fp; | 55 // pure JS code is being executed |
| 53 #ifdef __GNUC__ | 56 static void DoTraceHideCEntryFPAddress(unsigned int fp) { |
| 54 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); | 57 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |
| 55 #elif defined _MSC_VER | 58 CHECK(saved_c_frame_fp); |
| 56 __asm mov [fp], ebp // NOLINT | 59 *(Top::c_entry_fp_address()) = 0; |
| 57 #endif | |
| 58 DoTrace(fp); | 60 DoTrace(fp); |
| 61 *(Top::c_entry_fp_address()) = saved_c_frame_fp; |
| 59 } | 62 } |
| 60 | 63 |
| 61 | 64 |
| 62 static void CFunc(int i) { | 65 static void CheckRetAddrIsInFunction(const char* func_name, |
| 63 for (int j = i; j >= 0; --j) { | 66 unsigned int ret_addr, |
| 64 CFuncDoTrace(); | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 | |
| 69 static void CheckRetAddrIsInFunction(unsigned int ret_addr, | |
| 70 unsigned int func_start_addr, | 67 unsigned int func_start_addr, |
| 71 unsigned int func_len) { | 68 unsigned int func_len) { |
| 72 printf("CheckRetAddrIsInFunction: %08x %08x %08x\n", | 69 printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\n", |
| 73 func_start_addr, ret_addr, func_start_addr + func_len); | 70 func_name, func_start_addr, ret_addr, func_start_addr + func_len); |
| 74 CHECK_GE(ret_addr, func_start_addr); | 71 CHECK_GE(ret_addr, func_start_addr); |
| 75 CHECK_GE(func_start_addr + func_len, ret_addr); | 72 CHECK_GE(func_start_addr + func_len, ret_addr); |
| 76 } | 73 } |
| 77 | 74 |
| 78 | 75 |
| 79 #ifdef DEBUG | 76 static void CheckRetAddrIsInJSFunction(const char* func_name, |
| 80 static const int kMaxCFuncLen = 0x40; // seems enough for a small C function | 77 unsigned int ret_addr, |
| 81 | 78 Handle<JSFunction> func) { |
| 82 static void CheckRetAddrIsInCFunction(unsigned int ret_addr, | 79 v8::internal::Code* func_code = func->code(); |
| 83 unsigned int func_start_addr) { | 80 CheckRetAddrIsInFunction( |
| 84 CheckRetAddrIsInFunction(ret_addr, func_start_addr, kMaxCFuncLen); | 81 func_name, ret_addr, |
| 85 } | 82 reinterpret_cast<unsigned int>(func_code->instruction_start()), |
| 86 #endif | 83 func_code->ExecutableSize()); |
| 87 | |
| 88 | |
| 89 TEST(PureCStackTrace) { | |
| 90 TickSample sample; | |
| 91 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | |
| 92 InitTraceEnv(&tracer, &sample); | |
| 93 CFunc(0); | |
| 94 #ifdef DEBUG | |
| 95 // C stack trace works only in debug mode, in release mode EBP is | |
| 96 // usually treated as a general-purpose register | |
| 97 CHECK_GT(sample.frames_count, 0); | |
| 98 CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]), | |
| 99 reinterpret_cast<unsigned int>(&CFunc)); | |
| 100 #endif | |
| 101 } | 84 } |
| 102 | 85 |
| 103 | 86 |
| 104 // --- T r a c e E x t e n s i o n --- | 87 // --- T r a c e E x t e n s i o n --- |
| 105 | 88 |
| 106 class TraceExtension : public v8::Extension { | 89 class TraceExtension : public v8::Extension { |
| 107 public: | 90 public: |
| 108 TraceExtension() : v8::Extension("v8/trace", kSource) { } | 91 TraceExtension() : v8::Extension("v8/trace", kSource) { } |
| 109 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | 92 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
| 110 v8::Handle<v8::String> name); | 93 v8::Handle<String> name); |
| 111 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); | 94 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); |
| 95 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); |
| 112 private: | 96 private: |
| 97 static unsigned int GetFP(const v8::Arguments& args); |
| 113 static const char* kSource; | 98 static const char* kSource; |
| 114 }; | 99 }; |
| 115 | 100 |
| 116 | 101 |
| 117 const char* TraceExtension::kSource = "native function trace();"; | 102 const char* TraceExtension::kSource = |
| 103 "native function trace();" |
| 104 "native function js_trace();"; |
| 118 | 105 |
| 119 | 106 |
| 120 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( | 107 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( |
| 121 v8::Handle<v8::String> str) { | 108 v8::Handle<String> name) { |
| 122 return v8::FunctionTemplate::New(TraceExtension::Trace); | 109 if (name->Equals(String::New("trace"))) { |
| 110 return v8::FunctionTemplate::New(TraceExtension::Trace); |
| 111 } else if (name->Equals(String::New("js_trace"))) { |
| 112 return v8::FunctionTemplate::New(TraceExtension::JSTrace); |
| 113 } else { |
| 114 CHECK(false); |
| 115 return v8::Handle<v8::FunctionTemplate>(); |
| 116 } |
| 117 } |
| 118 |
| 119 |
| 120 unsigned int TraceExtension::GetFP(const v8::Arguments& args) { |
| 121 CHECK_EQ(1, args.Length()); |
| 122 unsigned int fp = args[0]->Int32Value() << 2; |
| 123 printf("Trace: %08x\n", fp); |
| 124 return fp; |
| 123 } | 125 } |
| 124 | 126 |
| 125 | 127 |
| 126 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { | 128 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
| 127 CHECK_EQ(1, args.Length()); | 129 DoTrace(GetFP(args)); |
| 128 unsigned int fp = args[0]->Int32Value() << 2; | |
| 129 printf("Trace: %08x\n", fp); | |
| 130 DoTrace(fp); | |
| 131 return v8::Undefined(); | 130 return v8::Undefined(); |
| 132 } | 131 } |
| 133 | 132 |
| 133 |
| 134 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |
| 135 DoTraceHideCEntryFPAddress(GetFP(args)); |
| 136 return v8::Undefined(); |
| 137 } |
| 138 |
| 134 | 139 |
| 135 static TraceExtension kTraceExtension; | 140 static TraceExtension kTraceExtension; |
| 136 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); | 141 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |
| 137 | 142 |
| 138 | 143 |
| 139 static void InitializeVM() { | 144 static void InitializeVM() { |
| 140 if (env.IsEmpty()) { | 145 if (env.IsEmpty()) { |
| 141 v8::HandleScope scope; | 146 v8::HandleScope scope; |
| 142 const char* extensions[] = { "v8/trace" }; | 147 const char* extensions[] = { "v8/trace" }; |
| 143 v8::ExtensionConfiguration config(1, extensions); | 148 v8::ExtensionConfiguration config(1, extensions); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 156 static void CompileRun(const char* source) { | 161 static void CompileRun(const char* source) { |
| 157 Script::Compile(String::New(source))->Run(); | 162 Script::Compile(String::New(source))->Run(); |
| 158 } | 163 } |
| 159 | 164 |
| 160 | 165 |
| 161 static Local<Value> GetGlobalProperty(const char* name) { | 166 static Local<Value> GetGlobalProperty(const char* name) { |
| 162 return env->Global()->Get(String::New(name)); | 167 return env->Global()->Get(String::New(name)); |
| 163 } | 168 } |
| 164 | 169 |
| 165 | 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 |
| 166 static void SetGlobalProperty(const char* name, Local<Value> value) { | 186 static void SetGlobalProperty(const char* name, Local<Value> value) { |
| 167 env->Global()->Set(String::New(name), value); | 187 env->Global()->Set(String::New(name), value); |
| 168 } | 188 } |
| 169 | 189 |
| 170 | 190 |
| 171 static bool Patch(byte* from, | 191 static bool Patch(byte* from, |
| 172 size_t num, | 192 size_t num, |
| 173 byte* original, | 193 byte* original, |
| 174 byte* patch, | 194 byte* patch, |
| 175 size_t patch_len) { | 195 size_t patch_len) { |
| 176 byte* to = from + num; | 196 byte* to = from + num; |
| 177 do { | 197 do { |
| 178 from = static_cast<byte*>(memchr(from, *original, to - from)); | 198 from = static_cast<byte*>(memchr(from, *original, to - from)); |
| 179 CHECK(from != NULL); | 199 CHECK(from != NULL); |
| 180 if (memcmp(original, from, patch_len) == 0) { | 200 if (memcmp(original, from, patch_len) == 0) { |
| 181 memcpy(from, patch, patch_len); | 201 memcpy(from, patch, patch_len); |
| 182 return true; | 202 return true; |
| 183 } else { | 203 } else { |
| 184 from++; | 204 from++; |
| 185 } | 205 } |
| 186 } while (to - from > 0); | 206 } while (to - from > 0); |
| 187 return false; | 207 return false; |
| 188 } | 208 } |
| 189 | 209 |
| 190 | 210 |
| 191 TEST(PureJSStackTrace) { | 211 // Creates a global function named 'func_name' that calls the tracing |
| 192 TickSample sample; | 212 // function 'trace_func_name' with an actual EBP register value, |
| 193 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | 213 // shifted right to be presented as Smi. |
| 194 InitTraceEnv(&tracer, &sample); | 214 static void CreateTraceCallerFunction(const char* func_name, |
| 195 | 215 const char* trace_func_name) { |
| 196 InitializeVM(); | 216 ::v8::internal::EmbeddedVector<char, 256> trace_call_buf; |
| 197 v8::HandleScope scope; | 217 ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name); |
| 198 Handle<JSFunction> call_trace = CompileFunction("trace(0x6666);"); | 218 Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |
| 199 CHECK(!call_trace.is_null()); | 219 CHECK(!func.is_null()); |
| 200 v8::internal::Code* call_trace_code = call_trace->code(); | 220 v8::internal::Code* func_code = func->code(); |
| 201 CHECK(call_trace_code->IsCode()); | 221 CHECK(func_code->IsCode()); |
| 202 | 222 |
| 203 // push 0xcccc (= 0x6666 << 1) | 223 // push 0xcccc (= 0x6666 << 1) |
| 204 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; | 224 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; |
| 205 // mov eax,ebp; shr eax; push eax; | 225 // mov eax,ebp; shr eax; push eax; |
| 206 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; | 226 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; |
| 207 // Patch generated code to replace pushing of a constant with | 227 // Patch generated code to replace pushing of a constant with |
| 208 // pushing of ebp contents in a Smi | 228 // pushing of ebp contents in a Smi |
| 209 CHECK(Patch(call_trace_code->instruction_start(), | 229 CHECK(Patch(func_code->instruction_start(), |
| 210 call_trace_code->instruction_size(), | 230 func_code->instruction_size(), |
| 211 original, patch, sizeof(patch))); | 231 original, patch, sizeof(patch))); |
| 212 | 232 |
| 213 SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace)); | 233 SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |
| 234 } |
| 214 | 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"); |
| 215 CompileRun( | 245 CompileRun( |
| 216 "function JSTrace() {" | 246 "function JSTrace() {" |
| 217 " JSFuncDoTrace();" | 247 " JSFuncDoTrace();" |
| 218 "};\n" | 248 "};\n" |
| 219 "JSTrace();"); | 249 "JSTrace();"); |
| 220 CHECK_GT(sample.frames_count, 1); | 250 CHECK_GT(sample.frames_count, 1); |
| 221 CheckRetAddrIsInFunction( | 251 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace" |
| 222 reinterpret_cast<unsigned int>(sample.stack[0]), | 252 CheckRetAddrIsInJSFunction("JSFuncDoTrace", |
| 223 reinterpret_cast<unsigned int>(call_trace_code->instruction_start()), | 253 reinterpret_cast<unsigned int>(sample.stack[0])); |
| 224 call_trace_code->instruction_size()); | 254 CheckRetAddrIsInJSFunction("JSTrace", |
| 225 Handle<JSFunction> js_trace(JSFunction::cast(*(v8::Utils::OpenHandle( | 255 reinterpret_cast<unsigned int>(sample.stack[1])); |
| 226 *GetGlobalProperty("JSTrace"))))); | |
| 227 v8::internal::Code* js_trace_code = js_trace->code(); | |
| 228 CheckRetAddrIsInFunction( | |
| 229 reinterpret_cast<unsigned int>(sample.stack[1]), | |
| 230 reinterpret_cast<unsigned int>(js_trace_code->instruction_start()), | |
| 231 js_trace_code->instruction_size()); | |
| 232 } | 256 } |
| 233 | 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() { |
| 285 unsigned int fp; |
| 286 #ifdef __GNUC__ |
| 287 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); |
| 288 #elif defined _MSC_VER |
| 289 __asm mov [fp], ebp // NOLINT |
| 290 #endif |
| 291 DoTrace(fp); |
| 292 } |
| 293 |
| 294 |
| 295 static int CFunc(int depth) { |
| 296 if (depth <= 0) { |
| 297 CFuncDoTrace(); |
| 298 return 0; |
| 299 } else { |
| 300 return CFunc(depth - 1) + 1; |
| 301 } |
| 302 } |
| 303 |
| 304 |
| 305 TEST(PureCStackTrace) { |
| 306 TickSample sample; |
| 307 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
| 308 InitTraceEnv(&tracer, &sample); |
| 309 // Check that sampler doesn't crash |
| 310 CHECK_EQ(10, CFunc(10)); |
| 311 } |
| 312 |
| 313 |
| 234 #endif // ENABLE_LOGGING_AND_PROFILING | 314 #endif // ENABLE_LOGGING_AND_PROFILING |
| OLD | NEW |