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 |