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 "log.h" | 11 #include "log.h" |
| 12 #include "top.h" |
12 #include "cctest.h" | 13 #include "cctest.h" |
13 | 14 |
14 using v8::Function; | 15 using v8::Function; |
15 using v8::Local; | 16 using v8::Local; |
16 using v8::Object; | 17 using v8::Object; |
17 using v8::Script; | 18 using v8::Script; |
18 using v8::String; | 19 using v8::String; |
19 using v8::Value; | 20 using v8::Value; |
20 | 21 |
21 using v8::internal::byte; | 22 using v8::internal::byte; |
22 using v8::internal::Handle; | 23 using v8::internal::Handle; |
23 using v8::internal::JSFunction; | 24 using v8::internal::JSFunction; |
24 using v8::internal::StackTracer; | 25 using v8::internal::StackTracer; |
25 using v8::internal::TickSample; | 26 using v8::internal::TickSample; |
| 27 using v8::internal::Top; |
26 | 28 |
27 | 29 |
28 static v8::Persistent<v8::Context> env; | 30 static v8::Persistent<v8::Context> env; |
29 | 31 |
30 | 32 |
31 static struct { | 33 static struct { |
32 StackTracer* tracer; | 34 StackTracer* tracer; |
33 TickSample* sample; | 35 TickSample* sample; |
34 } trace_env; | 36 } trace_env = { NULL, NULL }; |
35 | 37 |
36 | 38 |
37 static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { | 39 static void InitTraceEnv(StackTracer* tracer, TickSample* sample) { |
38 trace_env.tracer = tracer; | 40 trace_env.tracer = tracer; |
39 trace_env.sample = sample; | 41 trace_env.sample = sample; |
40 } | 42 } |
41 | 43 |
42 | 44 |
43 static void DoTrace(unsigned int fp) { | 45 static void DoTrace(unsigned int fp) { |
44 trace_env.sample->fp = fp; | 46 trace_env.sample->fp = fp; |
45 // something that is less than fp | 47 // sp is only used to define stack high bound |
46 trace_env.sample->sp = trace_env.sample->fp - 100; | 48 trace_env.sample->sp = |
| 49 reinterpret_cast<unsigned int>(trace_env.sample) - 10240; |
47 trace_env.tracer->Trace(trace_env.sample); | 50 trace_env.tracer->Trace(trace_env.sample); |
48 } | 51 } |
49 | 52 |
50 | 53 |
51 static void CFuncDoTrace() { | 54 // Hide c_entry_fp to emulate situation when sampling is done while |
52 unsigned int fp; | 55 // pure JS code is being executed |
53 #ifdef __GNUC__ | 56 static void DoTraceHideCEntryFPAddress(unsigned int fp) { |
54 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); | 57 v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |
55 #elif defined _MSC_VER | 58 CHECK(saved_c_frame_fp); |
56 __asm mov [fp], ebp // NOLINT | 59 *(Top::c_entry_fp_address()) = 0; |
57 #endif | |
58 DoTrace(fp); | 60 DoTrace(fp); |
| 61 *(Top::c_entry_fp_address()) = saved_c_frame_fp; |
59 } | 62 } |
60 | 63 |
61 | 64 |
62 static void CFunc(int i) { | 65 static void CheckRetAddrIsInFunction(const char* func_name, |
63 for (int j = i; j >= 0; --j) { | 66 unsigned int ret_addr, |
64 CFuncDoTrace(); | |
65 } | |
66 } | |
67 | |
68 | |
69 static void CheckRetAddrIsInFunction(unsigned int ret_addr, | |
70 unsigned int func_start_addr, | 67 unsigned int func_start_addr, |
71 unsigned int func_len) { | 68 unsigned int func_len) { |
72 printf("CheckRetAddrIsInFunction: %08x %08x %08x\n", | 69 printf("CheckRetAddrIsInFunction \"%s\": %08x %08x %08x\n", |
73 func_start_addr, ret_addr, func_start_addr + func_len); | 70 func_name, func_start_addr, ret_addr, func_start_addr + func_len); |
74 CHECK_GE(ret_addr, func_start_addr); | 71 CHECK_GE(ret_addr, func_start_addr); |
75 CHECK_GE(func_start_addr + func_len, ret_addr); | 72 CHECK_GE(func_start_addr + func_len, ret_addr); |
76 } | 73 } |
77 | 74 |
78 | 75 |
79 #ifdef DEBUG | 76 static void CheckRetAddrIsInJSFunction(const char* func_name, |
80 static const int kMaxCFuncLen = 0x40; // seems enough for a small C function | 77 unsigned int ret_addr, |
81 | 78 Handle<JSFunction> func) { |
82 static void CheckRetAddrIsInCFunction(unsigned int ret_addr, | 79 v8::internal::Code* func_code = func->code(); |
83 unsigned int func_start_addr) { | 80 CheckRetAddrIsInFunction( |
84 CheckRetAddrIsInFunction(ret_addr, func_start_addr, kMaxCFuncLen); | 81 func_name, ret_addr, |
85 } | 82 reinterpret_cast<unsigned int>(func_code->instruction_start()), |
86 #endif | 83 func_code->ExecutableSize()); |
87 | |
88 | |
89 TEST(PureCStackTrace) { | |
90 TickSample sample; | |
91 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | |
92 InitTraceEnv(&tracer, &sample); | |
93 CFunc(0); | |
94 #ifdef DEBUG | |
95 // C stack trace works only in debug mode, in release mode EBP is | |
96 // usually treated as a general-purpose register | |
97 CHECK_GT(sample.frames_count, 0); | |
98 CheckRetAddrIsInCFunction(reinterpret_cast<unsigned int>(sample.stack[0]), | |
99 reinterpret_cast<unsigned int>(&CFunc)); | |
100 #endif | |
101 } | 84 } |
102 | 85 |
103 | 86 |
104 // --- T r a c e E x t e n s i o n --- | 87 // --- T r a c e E x t e n s i o n --- |
105 | 88 |
106 class TraceExtension : public v8::Extension { | 89 class TraceExtension : public v8::Extension { |
107 public: | 90 public: |
108 TraceExtension() : v8::Extension("v8/trace", kSource) { } | 91 TraceExtension() : v8::Extension("v8/trace", kSource) { } |
109 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( | 92 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
110 v8::Handle<v8::String> name); | 93 v8::Handle<String> name); |
111 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); | 94 static v8::Handle<v8::Value> Trace(const v8::Arguments& args); |
| 95 static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); |
112 private: | 96 private: |
| 97 static unsigned int GetFP(const v8::Arguments& args); |
113 static const char* kSource; | 98 static const char* kSource; |
114 }; | 99 }; |
115 | 100 |
116 | 101 |
117 const char* TraceExtension::kSource = "native function trace();"; | 102 const char* TraceExtension::kSource = |
| 103 "native function trace();" |
| 104 "native function js_trace();"; |
118 | 105 |
119 | 106 |
120 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( | 107 v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( |
121 v8::Handle<v8::String> str) { | 108 v8::Handle<String> name) { |
122 return v8::FunctionTemplate::New(TraceExtension::Trace); | 109 if (name->Equals(String::New("trace"))) { |
| 110 return v8::FunctionTemplate::New(TraceExtension::Trace); |
| 111 } else if (name->Equals(String::New("js_trace"))) { |
| 112 return v8::FunctionTemplate::New(TraceExtension::JSTrace); |
| 113 } else { |
| 114 CHECK(false); |
| 115 return v8::Handle<v8::FunctionTemplate>(); |
| 116 } |
| 117 } |
| 118 |
| 119 |
| 120 unsigned int TraceExtension::GetFP(const v8::Arguments& args) { |
| 121 CHECK_EQ(1, args.Length()); |
| 122 unsigned int fp = args[0]->Int32Value() << 2; |
| 123 printf("Trace: %08x\n", fp); |
| 124 return fp; |
123 } | 125 } |
124 | 126 |
125 | 127 |
126 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { | 128 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
127 CHECK_EQ(1, args.Length()); | 129 DoTrace(GetFP(args)); |
128 unsigned int fp = args[0]->Int32Value() << 2; | |
129 printf("Trace: %08x\n", fp); | |
130 DoTrace(fp); | |
131 return v8::Undefined(); | 130 return v8::Undefined(); |
132 } | 131 } |
133 | 132 |
| 133 |
| 134 v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |
| 135 DoTraceHideCEntryFPAddress(GetFP(args)); |
| 136 return v8::Undefined(); |
| 137 } |
| 138 |
134 | 139 |
135 static TraceExtension kTraceExtension; | 140 static TraceExtension kTraceExtension; |
136 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); | 141 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |
137 | 142 |
138 | 143 |
139 static void InitializeVM() { | 144 static void InitializeVM() { |
140 if (env.IsEmpty()) { | 145 if (env.IsEmpty()) { |
141 v8::HandleScope scope; | 146 v8::HandleScope scope; |
142 const char* extensions[] = { "v8/trace" }; | 147 const char* extensions[] = { "v8/trace" }; |
143 v8::ExtensionConfiguration config(1, extensions); | 148 v8::ExtensionConfiguration config(1, extensions); |
(...skipping 12 matching lines...) Expand all Loading... |
156 static void CompileRun(const char* source) { | 161 static void CompileRun(const char* source) { |
157 Script::Compile(String::New(source))->Run(); | 162 Script::Compile(String::New(source))->Run(); |
158 } | 163 } |
159 | 164 |
160 | 165 |
161 static Local<Value> GetGlobalProperty(const char* name) { | 166 static Local<Value> GetGlobalProperty(const char* name) { |
162 return env->Global()->Get(String::New(name)); | 167 return env->Global()->Get(String::New(name)); |
163 } | 168 } |
164 | 169 |
165 | 170 |
| 171 static Handle<JSFunction> GetGlobalJSFunction(const char* name) { |
| 172 Handle<JSFunction> js_func(JSFunction::cast( |
| 173 *(v8::Utils::OpenHandle( |
| 174 *GetGlobalProperty(name))))); |
| 175 return js_func; |
| 176 } |
| 177 |
| 178 |
| 179 static void CheckRetAddrIsInJSFunction(const char* func_name, |
| 180 unsigned int ret_addr) { |
| 181 CheckRetAddrIsInJSFunction(func_name, ret_addr, |
| 182 GetGlobalJSFunction(func_name)); |
| 183 } |
| 184 |
| 185 |
166 static void SetGlobalProperty(const char* name, Local<Value> value) { | 186 static void SetGlobalProperty(const char* name, Local<Value> value) { |
167 env->Global()->Set(String::New(name), value); | 187 env->Global()->Set(String::New(name), value); |
168 } | 188 } |
169 | 189 |
170 | 190 |
171 static bool Patch(byte* from, | 191 static bool Patch(byte* from, |
172 size_t num, | 192 size_t num, |
173 byte* original, | 193 byte* original, |
174 byte* patch, | 194 byte* patch, |
175 size_t patch_len) { | 195 size_t patch_len) { |
176 byte* to = from + num; | 196 byte* to = from + num; |
177 do { | 197 do { |
178 from = static_cast<byte*>(memchr(from, *original, to - from)); | 198 from = static_cast<byte*>(memchr(from, *original, to - from)); |
179 CHECK(from != NULL); | 199 CHECK(from != NULL); |
180 if (memcmp(original, from, patch_len) == 0) { | 200 if (memcmp(original, from, patch_len) == 0) { |
181 memcpy(from, patch, patch_len); | 201 memcpy(from, patch, patch_len); |
182 return true; | 202 return true; |
183 } else { | 203 } else { |
184 from++; | 204 from++; |
185 } | 205 } |
186 } while (to - from > 0); | 206 } while (to - from > 0); |
187 return false; | 207 return false; |
188 } | 208 } |
189 | 209 |
190 | 210 |
191 TEST(PureJSStackTrace) { | 211 // Creates a global function named 'func_name' that calls the tracing |
192 TickSample sample; | 212 // function 'trace_func_name' with an actual EBP register value, |
193 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); | 213 // shifted right to be presented as Smi. |
194 InitTraceEnv(&tracer, &sample); | 214 static void CreateTraceCallerFunction(const char* func_name, |
195 | 215 const char* trace_func_name) { |
196 InitializeVM(); | 216 ::v8::internal::EmbeddedVector<char, 256> trace_call_buf; |
197 v8::HandleScope scope; | 217 ::v8::internal::OS::SNPrintF(trace_call_buf, "%s(0x6666);", trace_func_name); |
198 Handle<JSFunction> call_trace = CompileFunction("trace(0x6666);"); | 218 Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |
199 CHECK(!call_trace.is_null()); | 219 CHECK(!func.is_null()); |
200 v8::internal::Code* call_trace_code = call_trace->code(); | 220 v8::internal::Code* func_code = func->code(); |
201 CHECK(call_trace_code->IsCode()); | 221 CHECK(func_code->IsCode()); |
202 | 222 |
203 // push 0xcccc (= 0x6666 << 1) | 223 // push 0xcccc (= 0x6666 << 1) |
204 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; | 224 byte original[] = { 0x68, 0xcc, 0xcc, 0x00, 0x00 }; |
205 // mov eax,ebp; shr eax; push eax; | 225 // mov eax,ebp; shr eax; push eax; |
206 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; | 226 byte patch[] = { 0x89, 0xe8, 0xd1, 0xe8, 0x50 }; |
207 // Patch generated code to replace pushing of a constant with | 227 // Patch generated code to replace pushing of a constant with |
208 // pushing of ebp contents in a Smi | 228 // pushing of ebp contents in a Smi |
209 CHECK(Patch(call_trace_code->instruction_start(), | 229 CHECK(Patch(func_code->instruction_start(), |
210 call_trace_code->instruction_size(), | 230 func_code->instruction_size(), |
211 original, patch, sizeof(patch))); | 231 original, patch, sizeof(patch))); |
212 | 232 |
213 SetGlobalProperty("JSFuncDoTrace", v8::ToApi<Value>(call_trace)); | 233 SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |
| 234 } |
214 | 235 |
| 236 |
| 237 TEST(CFromJSStackTrace) { |
| 238 TickSample sample; |
| 239 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
| 240 InitTraceEnv(&tracer, &sample); |
| 241 |
| 242 InitializeVM(); |
| 243 v8::HandleScope scope; |
| 244 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |
215 CompileRun( | 245 CompileRun( |
216 "function JSTrace() {" | 246 "function JSTrace() {" |
217 " JSFuncDoTrace();" | 247 " JSFuncDoTrace();" |
218 "};\n" | 248 "};\n" |
219 "JSTrace();"); | 249 "JSTrace();"); |
220 CHECK_GT(sample.frames_count, 1); | 250 CHECK_GT(sample.frames_count, 1); |
221 CheckRetAddrIsInFunction( | 251 // Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace" |
222 reinterpret_cast<unsigned int>(sample.stack[0]), | 252 CheckRetAddrIsInJSFunction("JSFuncDoTrace", |
223 reinterpret_cast<unsigned int>(call_trace_code->instruction_start()), | 253 reinterpret_cast<unsigned int>(sample.stack[0])); |
224 call_trace_code->instruction_size()); | 254 CheckRetAddrIsInJSFunction("JSTrace", |
225 Handle<JSFunction> js_trace(JSFunction::cast(*(v8::Utils::OpenHandle( | 255 reinterpret_cast<unsigned int>(sample.stack[1])); |
226 *GetGlobalProperty("JSTrace"))))); | |
227 v8::internal::Code* js_trace_code = js_trace->code(); | |
228 CheckRetAddrIsInFunction( | |
229 reinterpret_cast<unsigned int>(sample.stack[1]), | |
230 reinterpret_cast<unsigned int>(js_trace_code->instruction_start()), | |
231 js_trace_code->instruction_size()); | |
232 } | 256 } |
233 | 257 |
| 258 |
| 259 TEST(PureJSStackTrace) { |
| 260 TickSample sample; |
| 261 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
| 262 InitTraceEnv(&tracer, &sample); |
| 263 |
| 264 InitializeVM(); |
| 265 v8::HandleScope scope; |
| 266 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); |
| 267 CompileRun( |
| 268 "function JSTrace() {" |
| 269 " JSFuncDoTrace();" |
| 270 "};\n" |
| 271 "function OuterJSTrace() {" |
| 272 " JSTrace();" |
| 273 "};\n" |
| 274 "OuterJSTrace();"); |
| 275 CHECK_GT(sample.frames_count, 1); |
| 276 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" |
| 277 CheckRetAddrIsInJSFunction("JSTrace", |
| 278 reinterpret_cast<unsigned int>(sample.stack[0])); |
| 279 CheckRetAddrIsInJSFunction("OuterJSTrace", |
| 280 reinterpret_cast<unsigned int>(sample.stack[1])); |
| 281 } |
| 282 |
| 283 |
| 284 static void CFuncDoTrace() { |
| 285 unsigned int fp; |
| 286 #ifdef __GNUC__ |
| 287 fp = reinterpret_cast<unsigned int>(__builtin_frame_address(0)); |
| 288 #elif defined _MSC_VER |
| 289 __asm mov [fp], ebp // NOLINT |
| 290 #endif |
| 291 DoTrace(fp); |
| 292 } |
| 293 |
| 294 |
| 295 static int CFunc(int depth) { |
| 296 if (depth <= 0) { |
| 297 CFuncDoTrace(); |
| 298 return 0; |
| 299 } else { |
| 300 return CFunc(depth - 1) + 1; |
| 301 } |
| 302 } |
| 303 |
| 304 |
| 305 TEST(PureCStackTrace) { |
| 306 TickSample sample; |
| 307 StackTracer tracer(reinterpret_cast<unsigned int>(&sample)); |
| 308 InitTraceEnv(&tracer, &sample); |
| 309 // Check that sampler doesn't crash |
| 310 CHECK_EQ(10, CFunc(10)); |
| 311 } |
| 312 |
| 313 |
234 #endif // ENABLE_LOGGING_AND_PROFILING | 314 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |