Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: test/cctest/test-log-stack-tracer.cc

Issue 160446: X64: enable stack sampling in profiler. (Closed)
Patch Set: Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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<uintptr_t>(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 // CodeGenerator::GenerateGetFramePointer pushes EBP / RBP value
134 // on stack. In 64-bit mode we can't use Smi operations code because
135 // they check that value is within Smi bounds.
136 Address fp = *reinterpret_cast<Address*>(*args[0]);
137 printf("Trace: %p\n", fp);
138 return fp;
139 }
140
141
142 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
143 DoTrace(GetFP(args));
144 return v8::Undefined();
145 }
146
147
148 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
149 DoTraceHideCEntryFPAddress(GetFP(args));
150 return v8::Undefined();
151 }
152
153
154 static Address GetJsEntrySp() {
155 CHECK_NE(NULL, Top::GetCurrentThread());
156 return Top::js_entry_sp(Top::GetCurrentThread());
157 }
158
159
160 v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) {
161 CHECK_NE(0, GetJsEntrySp());
162 return v8::Undefined();
163 }
164
165
166 static void CompileRun(const char* source) {
167 Script::Compile(String::New(source))->Run();
168 }
169
170
171 v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2(
172 const v8::Arguments& args) {
173 v8::HandleScope scope;
174 const Address js_entry_sp = GetJsEntrySp();
175 CHECK_NE(0, js_entry_sp);
176 CompileRun("js_entry_sp();");
177 CHECK_EQ(js_entry_sp, GetJsEntrySp());
178 return v8::Undefined();
179 }
180
181
182 static TraceExtension kTraceExtension;
183 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
184
185
186 static void InitializeVM() {
187 if (env.IsEmpty()) {
188 v8::HandleScope scope;
189 const char* extensions[] = { "v8/trace" };
190 v8::ExtensionConfiguration config(1, extensions);
191 env = v8::Context::New(&config);
192 }
193 v8::HandleScope scope;
194 env->Enter();
195 }
196
197
198 static Handle<JSFunction> CompileFunction(const char* source) {
199 return v8::Utils::OpenHandle(*Script::Compile(String::New(source)));
200 }
201
202
203 static Local<Value> GetGlobalProperty(const char* name) {
204 return env->Global()->Get(String::New(name));
205 }
206
207
208 static Handle<JSFunction> GetGlobalJSFunction(const char* name) {
209 Handle<JSFunction> js_func(JSFunction::cast(
210 *(v8::Utils::OpenHandle(
211 *GetGlobalProperty(name)))));
212 return js_func;
213 }
214
215
216 static void CheckRetAddrIsInJSFunction(const char* func_name,
217 Address ret_addr) {
218 CheckRetAddrIsInJSFunction(func_name, ret_addr,
219 GetGlobalJSFunction(func_name));
220 }
221
222
223 static void SetGlobalProperty(const char* name, Local<Value> value) {
224 env->Global()->Set(String::New(name), value);
225 }
226
227
228 static Handle<v8::internal::String> NewString(const char* s) {
229 return i::Factory::NewStringFromAscii(i::CStrVector(s));
230 }
231
232
233 namespace v8 {
234 namespace internal {
235
236 class CodeGeneratorPatcher {
237 public:
238 CodeGeneratorPatcher() {
239 CodeGenerator::InlineRuntimeLUT genGetFramePointer =
240 {&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"};
241 // _FastCharCodeAt is not used in our tests.
242 bool result = CodeGenerator::PatchInlineRuntimeEntry(
243 NewString("_FastCharCodeAt"),
244 genGetFramePointer, &oldInlineEntry);
245 CHECK(result);
246 }
247
248 ~CodeGeneratorPatcher() {
249 CHECK(CodeGenerator::PatchInlineRuntimeEntry(
250 NewString("_GetFramePointer"),
251 oldInlineEntry, NULL));
252 }
253
254 private:
255 CodeGenerator::InlineRuntimeLUT oldInlineEntry;
256 };
257
258 } } // namespace v8::internal
259
260
261 // Creates a global function named 'func_name' that calls the tracing
262 // function 'trace_func_name' with an actual EBP register value,
263 // shifted right to be presented as Smi.
264 static void CreateTraceCallerFunction(const char* func_name,
265 const char* trace_func_name) {
266 i::EmbeddedVector<char, 256> trace_call_buf;
267 i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name);
268
269 // Compile the script.
270 i::CodeGeneratorPatcher patcher;
271 bool allow_natives_syntax = i::FLAG_allow_natives_syntax;
272 i::FLAG_allow_natives_syntax = true;
273 Handle<JSFunction> func = CompileFunction(trace_call_buf.start());
274 CHECK(!func.is_null());
275 i::FLAG_allow_natives_syntax = allow_natives_syntax;
276
277 #ifdef DEBUG
278 v8::internal::Code* func_code = func->code();
279 CHECK(func_code->IsCode());
280 func_code->Print();
281 #endif
282
283 SetGlobalProperty(func_name, v8::ToApi<Value>(func));
284 }
285
286
287 TEST(CFromJSStackTrace) {
288 TickSample sample;
289 InitTraceEnv(&sample);
290
291 InitializeVM();
292 v8::HandleScope scope;
293 CreateTraceCallerFunction("JSFuncDoTrace", "trace");
294 CompileRun(
295 "function JSTrace() {"
296 " JSFuncDoTrace();"
297 "};\n"
298 "JSTrace();");
299 CHECK_GT(sample.frames_count, 1);
300 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
301 CheckRetAddrIsInJSFunction("JSFuncDoTrace",
302 sample.stack[0]);
303 CheckRetAddrIsInJSFunction("JSTrace",
304 sample.stack[1]);
305 }
306
307
308 TEST(PureJSStackTrace) {
309 TickSample sample;
310 InitTraceEnv(&sample);
311
312 InitializeVM();
313 v8::HandleScope scope;
314 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
315 CompileRun(
316 "function JSTrace() {"
317 " JSFuncDoTrace();"
318 "};\n"
319 "function OuterJSTrace() {"
320 " JSTrace();"
321 "};\n"
322 "OuterJSTrace();");
323 CHECK_GT(sample.frames_count, 1);
324 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
325 CheckRetAddrIsInJSFunction("JSTrace",
326 sample.stack[0]);
327 CheckRetAddrIsInJSFunction("OuterJSTrace",
328 sample.stack[1]);
329 }
330
331
332 static void CFuncDoTrace() {
333 Address fp;
334 #ifdef __GNUC__
335 fp = reinterpret_cast<Address>(__builtin_frame_address(0));
336 #elif defined _MSC_VER && defined V8_TARGET_ARCH_IA32
337 __asm mov [fp], ebp // NOLINT
338 #elif defined _MSC_VER && defined V8_TARGET_ARCH_X64
339 // FIXME: I haven't really tried to compile it.
340 __asm movq [fp], rbp // NOLINT
341 #endif
342 DoTrace(fp);
343 }
344
345
346 static int CFunc(int depth) {
347 if (depth <= 0) {
348 CFuncDoTrace();
349 return 0;
350 } else {
351 return CFunc(depth - 1) + 1;
352 }
353 }
354
355
356 TEST(PureCStackTrace) {
357 TickSample sample;
358 InitTraceEnv(&sample);
359 // Check that sampler doesn't crash
360 CHECK_EQ(10, CFunc(10));
361 }
362
363
364 TEST(JsEntrySp) {
365 InitializeVM();
366 v8::HandleScope scope;
367 CHECK_EQ(0, GetJsEntrySp());
368 CompileRun("a = 1; b = a + 1;");
369 CHECK_EQ(0, GetJsEntrySp());
370 CompileRun("js_entry_sp();");
371 CHECK_EQ(0, GetJsEntrySp());
372 CompileRun("js_entry_sp_level2();");
373 CHECK_EQ(0, GetJsEntrySp());
374 }
375
376 #endif // ENABLE_LOGGING_AND_PROFILING
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698