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 |