blob: 22b1600b980badf4b7d42496de8a4df27ae66817 [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.orgb3284ad2009-05-18 06:12:45 +000016#include "register-allocator-inl.h"
kasperl@chromium.org061ef742009-02-27 12:16:20 +000017
18using v8::Function;
19using v8::Local;
20using v8::Object;
21using v8::Script;
22using v8::String;
23using v8::Value;
24
25using v8::internal::byte;
ager@chromium.org9085a012009-05-11 19:22:57 +000026using v8::internal::Address;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000027using v8::internal::Handle;
28using v8::internal::JSFunction;
29using v8::internal::StackTracer;
30using v8::internal::TickSample;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000031using v8::internal::Top;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000032
ager@chromium.org9085a012009-05-11 19:22:57 +000033namespace i = v8::internal;
34
kasperl@chromium.org061ef742009-02-27 12:16:20 +000035
36static v8::Persistent<v8::Context> env;
37
38
39static struct {
40 StackTracer* tracer;
41 TickSample* sample;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000042} trace_env = { NULL, NULL };
kasperl@chromium.org061ef742009-02-27 12:16:20 +000043
44
45static void InitTraceEnv(StackTracer* tracer, TickSample* sample) {
46 trace_env.tracer = tracer;
47 trace_env.sample = sample;
48}
49
50
ager@chromium.org9085a012009-05-11 19:22:57 +000051static void DoTrace(Address fp) {
52 trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000053 // sp is only used to define stack high bound
54 trace_env.sample->sp =
55 reinterpret_cast<unsigned int>(trace_env.sample) - 10240;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000056 trace_env.tracer->Trace(trace_env.sample);
57}
58
59
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000060// Hide c_entry_fp to emulate situation when sampling is done while
61// pure JS code is being executed
ager@chromium.org9085a012009-05-11 19:22:57 +000062static void DoTraceHideCEntryFPAddress(Address fp) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000063 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address());
64 CHECK(saved_c_frame_fp);
65 *(Top::c_entry_fp_address()) = 0;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000066 DoTrace(fp);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000067 *(Top::c_entry_fp_address()) = saved_c_frame_fp;
kasperl@chromium.org061ef742009-02-27 12:16:20 +000068}
69
70
ager@chromium.org9085a012009-05-11 19:22:57 +000071static void CheckRetAddrIsInFunction(const char* func_name,
72 Address ret_addr,
73 Address func_start_addr,
74 unsigned int func_len) {
75 printf("CheckRetAddrIsInFunction \"%s\": %p %p %p\n",
76 func_name, func_start_addr, ret_addr, func_start_addr + func_len);
77 CHECK_GE(ret_addr, func_start_addr);
78 CHECK_GE(func_start_addr + func_len, ret_addr);
79}
80
81
82static void CheckRetAddrIsInJSFunction(const char* func_name,
83 Address ret_addr,
84 Handle<JSFunction> func) {
85 v8::internal::Code* func_code = func->code();
86 CheckRetAddrIsInFunction(
87 func_name, ret_addr,
88 func_code->instruction_start(),
89 func_code->ExecutableSize());
90}
91
92
kasperl@chromium.org061ef742009-02-27 12:16:20 +000093// --- T r a c e E x t e n s i o n ---
94
95class TraceExtension : public v8::Extension {
96 public:
97 TraceExtension() : v8::Extension("v8/trace", kSource) { }
98 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000099 v8::Handle<String> name);
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000100 static v8::Handle<v8::Value> Trace(const v8::Arguments& args);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000101 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args);
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000102 private:
ager@chromium.org9085a012009-05-11 19:22:57 +0000103 static Address GetFP(const v8::Arguments& args);
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000104 static const char* kSource;
105};
106
107
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000108const char* TraceExtension::kSource =
109 "native function trace();"
110 "native function js_trace();";
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000111
112
113v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000114 v8::Handle<String> name) {
115 if (name->Equals(String::New("trace"))) {
116 return v8::FunctionTemplate::New(TraceExtension::Trace);
117 } else if (name->Equals(String::New("js_trace"))) {
118 return v8::FunctionTemplate::New(TraceExtension::JSTrace);
119 } else {
120 CHECK(false);
121 return v8::Handle<v8::FunctionTemplate>();
122 }
123}
124
125
ager@chromium.org9085a012009-05-11 19:22:57 +0000126Address TraceExtension::GetFP(const v8::Arguments& args) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000127 CHECK_EQ(1, args.Length());
ager@chromium.org9085a012009-05-11 19:22:57 +0000128 Address fp = reinterpret_cast<Address>(args[0]->Int32Value() << 2);
129 printf("Trace: %p\n", fp);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000130 return fp;
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000131}
132
133
134v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000135 DoTrace(GetFP(args));
136 return v8::Undefined();
137}
138
139
140v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) {
141 DoTraceHideCEntryFPAddress(GetFP(args));
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000142 return v8::Undefined();
143}
144
145
146static TraceExtension kTraceExtension;
147v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension);
148
149
ager@chromium.org9085a012009-05-11 19:22:57 +0000150static void InitializeVM() {
151 if (env.IsEmpty()) {
152 v8::HandleScope scope;
153 const char* extensions[] = { "v8/trace" };
154 v8::ExtensionConfiguration config(1, extensions);
155 env = v8::Context::New(&config);
156 }
157 v8::HandleScope scope;
158 env->Enter();
159}
160
161
162static Handle<JSFunction> CompileFunction(const char* source) {
163 return v8::Utils::OpenHandle(*Script::Compile(String::New(source)));
164}
165
166
167static void CompileRun(const char* source) {
168 Script::Compile(String::New(source))->Run();
169}
170
171
172static Local<Value> GetGlobalProperty(const char* name) {
173 return env->Global()->Get(String::New(name));
174}
175
176
177static Handle<JSFunction> GetGlobalJSFunction(const char* name) {
178 Handle<JSFunction> js_func(JSFunction::cast(
179 *(v8::Utils::OpenHandle(
180 *GetGlobalProperty(name)))));
181 return js_func;
182}
183
184
185static void CheckRetAddrIsInJSFunction(const char* func_name,
186 Address ret_addr) {
187 CheckRetAddrIsInJSFunction(func_name, ret_addr,
188 GetGlobalJSFunction(func_name));
189}
190
191
192static void SetGlobalProperty(const char* name, Local<Value> value) {
193 env->Global()->Set(String::New(name), value);
194}
195
196
197static Handle<v8::internal::String> NewString(const char* s) {
198 return i::Factory::NewStringFromAscii(i::CStrVector(s));
199}
200
201
202namespace v8 { namespace internal {
203
204class CodeGeneratorPatcher {
205 public:
206 CodeGeneratorPatcher() {
207 CodeGenerator::InlineRuntimeLUT genGetFramePointer =
208 {&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"};
209 // _FastCharCodeAt is not used in our tests.
210 bool result = CodeGenerator::PatchInlineRuntimeEntry(
211 NewString("_FastCharCodeAt"),
212 genGetFramePointer, &oldInlineEntry);
213 CHECK(result);
214 }
215
216 ~CodeGeneratorPatcher() {
217 CHECK(CodeGenerator::PatchInlineRuntimeEntry(
218 NewString("_GetFramePointer"),
219 oldInlineEntry, NULL));
220 }
221
222 private:
223 CodeGenerator::InlineRuntimeLUT oldInlineEntry;
224};
225
226} } // namespace v8::internal
227
228
229// Creates a global function named 'func_name' that calls the tracing
230// function 'trace_func_name' with an actual EBP register value,
231// shifted right to be presented as Smi.
232static void CreateTraceCallerFunction(const char* func_name,
233 const char* trace_func_name) {
234 i::EmbeddedVector<char, 256> trace_call_buf;
235 i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name);
236
237 // Compile the script.
238 i::CodeGeneratorPatcher patcher;
239 bool allow_natives_syntax = i::FLAG_allow_natives_syntax;
240 i::FLAG_allow_natives_syntax = true;
241 Handle<JSFunction> func = CompileFunction(trace_call_buf.start());
242 CHECK(!func.is_null());
243 i::FLAG_allow_natives_syntax = allow_natives_syntax;
244
245#ifdef DEBUG
246 v8::internal::Code* func_code = func->code();
247 CHECK(func_code->IsCode());
248 func_code->Print();
249#endif
250
251 SetGlobalProperty(func_name, v8::ToApi<Value>(func));
252}
253
254
255TEST(CFromJSStackTrace) {
256 TickSample sample;
257 StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
258 InitTraceEnv(&tracer, &sample);
259
260 InitializeVM();
261 v8::HandleScope scope;
262 CreateTraceCallerFunction("JSFuncDoTrace", "trace");
263 CompileRun(
264 "function JSTrace() {"
265 " JSFuncDoTrace();"
266 "};\n"
267 "JSTrace();");
268 CHECK_GT(sample.frames_count, 1);
269 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
270 CheckRetAddrIsInJSFunction("JSFuncDoTrace",
271 sample.stack[0]);
272 CheckRetAddrIsInJSFunction("JSTrace",
273 sample.stack[1]);
274}
275
276
277TEST(PureJSStackTrace) {
278 TickSample sample;
279 StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
280 InitTraceEnv(&tracer, &sample);
281
282 InitializeVM();
283 v8::HandleScope scope;
284 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace");
285 CompileRun(
286 "function JSTrace() {"
287 " JSFuncDoTrace();"
288 "};\n"
289 "function OuterJSTrace() {"
290 " JSTrace();"
291 "};\n"
292 "OuterJSTrace();");
293 CHECK_GT(sample.frames_count, 1);
294 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
295 CheckRetAddrIsInJSFunction("JSTrace",
296 sample.stack[0]);
297 CheckRetAddrIsInJSFunction("OuterJSTrace",
298 sample.stack[1]);
299}
300
301
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000302static void CFuncDoTrace() {
ager@chromium.org9085a012009-05-11 19:22:57 +0000303 Address fp;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000304#ifdef __GNUC__
ager@chromium.org9085a012009-05-11 19:22:57 +0000305 fp = reinterpret_cast<Address>(__builtin_frame_address(0));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000306#elif defined _MSC_VER
307 __asm mov [fp], ebp // NOLINT
308#endif
309 DoTrace(fp);
310}
311
312
313static int CFunc(int depth) {
314 if (depth <= 0) {
315 CFuncDoTrace();
316 return 0;
317 } else {
318 return CFunc(depth - 1) + 1;
319 }
320}
321
322
323TEST(PureCStackTrace) {
324 TickSample sample;
ager@chromium.org9085a012009-05-11 19:22:57 +0000325 StackTracer tracer(reinterpret_cast<uintptr_t>(&sample));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000326 InitTraceEnv(&tracer, &sample);
327 // Check that sampler doesn't crash
328 CHECK_EQ(10, CFunc(10));
329}
330
331
kasperl@chromium.org061ef742009-02-27 12:16:20 +0000332#endif // ENABLE_LOGGING_AND_PROFILING