blob: 43cb294b17fcb8881de91e52c5fbdde2de9c2bd7 [file] [log] [blame]
kasperl@chromium.org061ef742009-02-27 12:16:20 +00001// 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
ager@chromium.org9085a012009-05-11 19:22:57 +000011#include "codegen.h"
kasperl@chromium.org061ef742009-02-27 12:16:20 +000012#include "log.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000013#include "top.h"
kasperl@chromium.org061ef742009-02-27 12:16:20 +000014#include "cctest.h"
ager@chromium.org9085a012009-05-11 19:22:57 +000015#include "disassembler.h"
kasperl@chromium.org061ef742009-02-27 12:16:20 +000016
17using v8::Function;
18using v8::Local;
19using v8::Object;
20using v8::Script;
21using v8::String;
22using v8::Value;
23
24using v8::internal::byte;
ager@chromium.org9085a012009-05-11 19:22:57 +000025using v8::internal::Address;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000026using v8::internal::Handle;
27using v8::internal::JSFunction;
28using v8::internal::StackTracer;
29using v8::internal::TickSample;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000030using v8::internal::Top;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000031
ager@chromium.org9085a012009-05-11 19:22:57 +000032namespace i = v8::internal;
33
kasperl@chromium.org061ef742009-02-27 12:16:20 +000034
35static v8::Persistent<v8::Context> env;
36
37
38static struct {
39 StackTracer* tracer;
40 TickSample* sample;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000041} trace_env = { NULL, NULL };
kasperl@chromium.org061ef742009-02-27 12:16:20 +000042
43
44static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
45 trace_env.tracer = tracer;
46 trace_env.sample = sample;
47}
48
49
ager@chromium.org9085a012009-05-11 19:22:57 +000050static void DoTrace(Address fp) {
51 trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000052 // sp is only used to define stack high bound
53 trace_env.sample->sp =
54 reinterpret_cast<unsigned int>(trace_env.sample) - 10240;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000055 trace_env.tracer->Trace(trace_env.sample);
56}
57
58
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000059// Hide c_entry_fp to emulate situation when sampling is done while
60// pure JS code is being executed
ager@chromium.org9085a012009-05-11 19:22:57 +000061static void DoTraceHideCEntryFPAddress(Address fp) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000062 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
63 CHECK(saved_c_frame_fp);
64 *(Top::c_entry_fp_address()) = 0;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000065 DoTrace(fp);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000066 *(Top::c_entry_fp_address()) = saved_c_frame_fp;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000067}
68
69
ager@chromium.org9085a012009-05-11 19:22:57 +000070static 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
81static 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
kasperl@chromium.org061ef742009-02-27 12:16:20 +000092// --- T r a c e E x t e n s i o n ---
93
94class TraceExtension : public v8::Extension {
95 public:
96 TraceExtension() : v8::Extension("v8/trace", kSource) { }
97 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000098 v8::Handle<String> name);
kasperl@chromium.org061ef742009-02-27 12:16:20 +000099 static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000100 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000101 private:
ager@chromium.org9085a012009-05-11 19:22:57 +0000102 static Address GetFP(const v8::Arguments& args);
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000103 static const char* kSource;
104};
105
106
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000107const char* TraceExtension::kSource =
108 "native function trace();"
109 "native function js_trace();";
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000110
111
112v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000113 v8::Handle<String> name) {
114 if (name->Equals(String::New("trace"))) {
115 return v8::FunctionTemplate::New(TraceExtension::Trace);
116 } else if (name->Equals(String::New("js_trace"))) {
117 return v8::FunctionTemplate::New(TraceExtension::JSTrace);
118 } else {
119 CHECK(false);
120 return v8::Handle<v8::FunctionTemplate>();
121 }
122}
123
124
ager@chromium.org9085a012009-05-11 19:22:57 +0000125Address TraceExtension::GetFP(const v8::Arguments& args) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000126 CHECK_EQ(1, args.Length());
ager@chromium.org9085a012009-05-11 19:22:57 +0000127 Address fp = reinterpret_cast<Address>(args[0]->Int32Value() << 2);
128 printf("Trace: %p\n", fp);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000129 return fp;
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000130}
131
132
133v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000134 DoTrace(GetFP(args));
135 return v8::Undefined();
136}
137
138
139v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
140 DoTraceHideCEntryFPAddress(GetFP(args));
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000141 return v8::Undefined();
142}
143
144
145static TraceExtension kTraceExtension;
146v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
147
148
ager@chromium.org9085a012009-05-11 19:22:57 +0000149static 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
161static Handle<JSFunction> CompileFunction(const char* source) {
162 return v8::Utils::OpenHandle(*Script::Compile(String::New(source)));
163}
164
165
166static void CompileRun(const char* source) {
167 Script::Compile(String::New(source))->Run();
168}
169
170
171static Local<Value> GetGlobalProperty(const char* name) {
172 return env->Global()->Get(String::New(name));
173}
174
175
176static 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
184static void CheckRetAddrIsInJSFunction(const char* func_name,
185 Address ret_addr) {
186 CheckRetAddrIsInJSFunction(func_name, ret_addr,
187 GetGlobalJSFunction(func_name));
188}
189
190
191static void SetGlobalProperty(const char* name, Local<Value> value) {
192 env->Global()->Set(String::New(name), value);
193}
194
195
196static Handle<v8::internal::String> NewString(const char* s) {
197 return i::Factory::NewStringFromAscii(i::CStrVector(s));
198}
199
200
201namespace v8 { namespace internal {
202
203class 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.
231static 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
254TEST(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
276TEST(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
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000301static void CFuncDoTrace() {
ager@chromium.org9085a012009-05-11 19:22:57 +0000302 Address fp;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000303#ifdef __GNUC__
ager@chromium.org9085a012009-05-11 19:22:57 +0000304 fp = reinterpret_cast<Address>(__builtin_frame_address(0));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000305#elif defined _MSC_VER
306 __asm mov [fp], ebp // NOLINT
307#endif
308 DoTrace(fp);
309}
310
311
312static int CFunc(int depth) {
313 if (depth <= 0) {
314 CFuncDoTrace();
315 return 0;
316 } else {
317 return CFunc(depth - 1) + 1;
318 }
319}
320
321
322TEST(PureCStackTrace) {
323 TickSample sample;
ager@chromium.org9085a012009-05-11 19:22:57 +0000324 StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000325 InitTraceEnv(&tracer, &sample);
326 // Check that sampler doesn't crash
327 CHECK_EQ(10, CFunc(10));
328}
329
330
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000331#endif // ENABLE_LOGGING_AND_PROFILING