OLD | NEW |
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are |
| 4 // met: |
| 5 // |
| 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. |
| 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
2 // | 27 // |
3 // Tests of profiler-related functions from log.h | 28 // Tests of profiler-related functions from log.h |
4 | 29 |
5 #ifdef ENABLE_LOGGING_AND_PROFILING | 30 #ifdef ENABLE_LOGGING_AND_PROFILING |
6 | 31 |
7 #include <stdlib.h> | 32 #include <stdlib.h> |
8 | 33 |
9 #include "v8.h" | 34 #include "v8.h" |
10 | 35 |
11 #include "codegen.h" | 36 #include "codegen.h" |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 } else if (name->Equals(String::New("js_entry_sp_level2"))) { | 125 } else if (name->Equals(String::New("js_entry_sp_level2"))) { |
101 return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2); | 126 return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2); |
102 } else { | 127 } else { |
103 CHECK(false); | 128 CHECK(false); |
104 return v8::Handle<v8::FunctionTemplate>(); | 129 return v8::Handle<v8::FunctionTemplate>(); |
105 } | 130 } |
106 } | 131 } |
107 | 132 |
108 | 133 |
109 Address TraceExtension::GetFP(const v8::Arguments& args) { | 134 Address TraceExtension::GetFP(const v8::Arguments& args) { |
110 CHECK_EQ(1, args.Length()); | 135 // Convert frame pointer from encoding as smis in the arguments to a pointer. |
111 // CodeGenerator::GenerateGetFramePointer pushes EBP / RBP value | 136 CHECK_EQ(2, args.Length()); // Ignore second argument on 32-bit platform. |
112 // on stack. In 64-bit mode we can't use Smi operations code because | 137 #if defined(V8_HOST_ARCH_32_BIT) |
113 // they check that value is within Smi bounds. | |
114 Address fp = *reinterpret_cast<Address*>(*args[0]); | 138 Address fp = *reinterpret_cast<Address*>(*args[0]); |
| 139 #elif defined(V8_HOST_ARCH_64_BIT) |
| 140 int64_t low_bits = *reinterpret_cast<uint64_t*>(*args[0]) >> 32; |
| 141 int64_t high_bits = *reinterpret_cast<uint64_t*>(*args[1]); |
| 142 Address fp = reinterpret_cast<Address>(high_bits | low_bits); |
| 143 #else |
| 144 #error Host architecture is neither 32-bit nor 64-bit. |
| 145 #endif |
115 printf("Trace: %p\n", fp); | 146 printf("Trace: %p\n", fp); |
116 return fp; | 147 return fp; |
117 } | 148 } |
118 | 149 |
119 | 150 |
120 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { | 151 v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
121 DoTrace(GetFP(args)); | 152 DoTrace(GetFP(args)); |
122 return v8::Undefined(); | 153 return v8::Undefined(); |
123 } | 154 } |
124 | 155 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 static void SetGlobalProperty(const char* name, Local<Value> value) { | 234 static void SetGlobalProperty(const char* name, Local<Value> value) { |
204 env->Global()->Set(String::New(name), value); | 235 env->Global()->Set(String::New(name), value); |
205 } | 236 } |
206 | 237 |
207 | 238 |
208 static Handle<v8::internal::String> NewString(const char* s) { | 239 static Handle<v8::internal::String> NewString(const char* s) { |
209 return i::Factory::NewStringFromAscii(i::CStrVector(s)); | 240 return i::Factory::NewStringFromAscii(i::CStrVector(s)); |
210 } | 241 } |
211 | 242 |
212 | 243 |
213 namespace v8 { | 244 // This C++ function is called as a constructor, to grab the frame pointer |
214 namespace internal { | 245 // from the calling function. When this function runs, the stack contains |
| 246 // a C_Entry frame and a Construct frame above the calling function's frame. |
| 247 static v8::Handle<Value> construct_call(const v8::Arguments& args) { |
| 248 i::StackFrameIterator frame_iterator; |
| 249 CHECK(frame_iterator.frame()->is_exit()); |
| 250 frame_iterator.Advance(); |
| 251 CHECK(frame_iterator.frame()->is_construct()); |
| 252 frame_iterator.Advance(); |
| 253 i::StackFrame* calling_frame = frame_iterator.frame(); |
| 254 CHECK(calling_frame->is_java_script()); |
215 | 255 |
216 class CodeGeneratorPatcher { | 256 #if defined(V8_HOST_ARCH_32_BIT) |
217 public: | 257 int32_t low_bits = reinterpret_cast<intptr_t>(calling_frame->fp()); |
218 CodeGeneratorPatcher() { | 258 args.This()->Set(v8_str("low_bits"), v8_num(low_bits >> 1)); |
219 CodeGenerator::InlineRuntimeLUT gen_get_frame_pointer = | 259 #elif defined(V8_HOST_ARCH_64_BIT) |
220 {&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer", 0}; | 260 int32_t low_bits = reinterpret_cast<uintptr_t>(calling_frame->fp()); |
221 // _RandomHeapNumber is just used as a dummy function that has zero | 261 int32_t high_bits = reinterpret_cast<uintptr_t>(calling_frame->fp()) >> 32; |
222 // arguments, the same as the _GetFramePointer function we actually patch | 262 args.This()->Set(v8_str("low_bits"), v8_num(low_bits)); |
223 // in. | 263 args.This()->Set(v8_str("high_bits"), v8_num(high_bits)); |
224 bool result = CodeGenerator::PatchInlineRuntimeEntry( | 264 #else |
225 NewString("_RandomHeapNumber"), | 265 #error Host architecture is neither 32-bit nor 64-bit. |
226 gen_get_frame_pointer, &old_inline_entry); | 266 #endif |
227 CHECK(result); | 267 return args.This(); |
228 } | 268 } |
229 | 269 |
230 ~CodeGeneratorPatcher() { | |
231 CHECK(CodeGenerator::PatchInlineRuntimeEntry( | |
232 NewString("_GetFramePointer"), | |
233 old_inline_entry, NULL)); | |
234 } | |
235 | 270 |
236 private: | 271 // Use the API to create a JSFunction object that calls the above C++ function. |
237 CodeGenerator::InlineRuntimeLUT old_inline_entry; | 272 void CreateFramePointerGrabberConstructor(const char* constructor_name) { |
238 }; | 273 Local<v8::FunctionTemplate> constructor_template = |
239 | 274 v8::FunctionTemplate::New(construct_call); |
240 } } // namespace v8::internal | 275 constructor_template->SetClassName(v8_str("FPGrabber")); |
| 276 Local<Function> fun = constructor_template->GetFunction(); |
| 277 env->Global()->Set(v8_str(constructor_name), fun); |
| 278 } |
241 | 279 |
242 | 280 |
243 // Creates a global function named 'func_name' that calls the tracing | 281 // Creates a global function named 'func_name' that calls the tracing |
244 // function 'trace_func_name' with an actual EBP register value, | 282 // function 'trace_func_name' with an actual EBP register value, |
245 // shifted right to be presented as Smi. | 283 // encoded as one or two Smis. |
246 static void CreateTraceCallerFunction(const char* func_name, | 284 static void CreateTraceCallerFunction(const char* func_name, |
247 const char* trace_func_name) { | 285 const char* trace_func_name) { |
248 i::EmbeddedVector<char, 256> trace_call_buf; | 286 i::EmbeddedVector<char, 256> trace_call_buf; |
249 i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name); | 287 i::OS::SNPrintF(trace_call_buf, |
| 288 "fp = new FPGrabber(); %s(fp.low_bits, fp.high_bits);", |
| 289 trace_func_name); |
| 290 |
| 291 // Create the FPGrabber function, which grabs the caller's frame pointer |
| 292 // when called as a constructor. |
| 293 CreateFramePointerGrabberConstructor("FPGrabber"); |
250 | 294 |
251 // Compile the script. | 295 // Compile the script. |
252 i::CodeGeneratorPatcher patcher; | |
253 bool allow_natives_syntax = i::FLAG_allow_natives_syntax; | |
254 i::FLAG_allow_natives_syntax = true; | |
255 Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); | 296 Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |
256 CHECK(!func.is_null()); | 297 CHECK(!func.is_null()); |
257 i::FLAG_allow_natives_syntax = allow_natives_syntax; | |
258 func->shared()->set_name(*NewString(func_name)); | 298 func->shared()->set_name(*NewString(func_name)); |
259 | 299 |
260 #ifdef DEBUG | 300 #ifdef DEBUG |
261 v8::internal::Code* func_code = func->code(); | 301 v8::internal::Code* func_code = func->code(); |
262 CHECK(func_code->IsCode()); | 302 CHECK(func_code->IsCode()); |
263 func_code->Print(); | 303 func_code->Print(); |
264 #endif | 304 #endif |
265 | 305 |
266 SetGlobalProperty(func_name, v8::ToApi<Value>(func)); | 306 SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |
267 CHECK_EQ(*func, *GetGlobalJSFunction(func_name)); | 307 CHECK_EQ(*func, *GetGlobalJSFunction(func_name)); |
268 } | 308 } |
269 | 309 |
270 | 310 |
271 // This test verifies that stack tracing works when called during | 311 // This test verifies that stack tracing works when called during |
272 // execution of a native function called from JS code. In this case, | 312 // execution of a native function called from JS code. In this case, |
273 // StackTracer uses Top::c_entry_fp as a starting point for stack | 313 // StackTracer uses Top::c_entry_fp as a starting point for stack |
274 // walking. | 314 // walking. |
275 TEST(CFromJSStackTrace) { | 315 TEST(CFromJSStackTrace) { |
276 // TODO(711): The hack of replacing the inline runtime function | |
277 // RandomHeapNumber with GetFrameNumber does not work with the way | |
278 // the full compiler generates inline runtime calls. | |
279 i::FLAG_full_compiler = false; | |
280 i::FLAG_always_full_compiler = false; | |
281 | |
282 TickSample sample; | 316 TickSample sample; |
283 InitTraceEnv(&sample); | 317 InitTraceEnv(&sample); |
284 | 318 |
285 InitializeVM(); | 319 InitializeVM(); |
286 v8::HandleScope scope; | 320 v8::HandleScope scope; |
287 // Create global function JSFuncDoTrace which calls | 321 // Create global function JSFuncDoTrace which calls |
288 // extension function trace() with the current frame pointer value. | 322 // extension function trace() with the current frame pointer value. |
289 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); | 323 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |
290 Local<Value> result = CompileRun( | 324 Local<Value> result = CompileRun( |
291 "function JSTrace() {" | 325 "function JSTrace() {" |
(...skipping 15 matching lines...) Expand all Loading... |
307 CheckObjectIsJSFunction("JSTrace", sample.stack[1]); | 341 CheckObjectIsJSFunction("JSTrace", sample.stack[1]); |
308 } | 342 } |
309 | 343 |
310 | 344 |
311 // This test verifies that stack tracing works when called during | 345 // This test verifies that stack tracing works when called during |
312 // execution of JS code. However, as calling StackTracer requires | 346 // execution of JS code. However, as calling StackTracer requires |
313 // entering native code, we can only emulate pure JS by erasing | 347 // entering native code, we can only emulate pure JS by erasing |
314 // Top::c_entry_fp value. In this case, StackTracer uses passed frame | 348 // Top::c_entry_fp value. In this case, StackTracer uses passed frame |
315 // pointer value as a starting point for stack walking. | 349 // pointer value as a starting point for stack walking. |
316 TEST(PureJSStackTrace) { | 350 TEST(PureJSStackTrace) { |
317 // TODO(711): The hack of replacing the inline runtime function | |
318 // RandomHeapNumber with GetFrameNumber does not work with the way | |
319 // the full compiler generates inline runtime calls. | |
320 i::FLAG_full_compiler = false; | |
321 i::FLAG_always_full_compiler = false; | |
322 | |
323 TickSample sample; | 351 TickSample sample; |
324 InitTraceEnv(&sample); | 352 InitTraceEnv(&sample); |
325 | 353 |
326 InitializeVM(); | 354 InitializeVM(); |
327 v8::HandleScope scope; | 355 v8::HandleScope scope; |
328 // Create global function JSFuncDoTrace which calls | 356 // Create global function JSFuncDoTrace which calls |
329 // extension function js_trace() with the current frame pointer value. | 357 // extension function js_trace() with the current frame pointer value. |
330 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); | 358 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); |
331 Local<Value> result = CompileRun( | 359 Local<Value> result = CompileRun( |
332 "function JSTrace() {" | 360 "function JSTrace() {" |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 CHECK_EQ(0, GetJsEntrySp()); | 428 CHECK_EQ(0, GetJsEntrySp()); |
401 CompileRun("a = 1; b = a + 1;"); | 429 CompileRun("a = 1; b = a + 1;"); |
402 CHECK_EQ(0, GetJsEntrySp()); | 430 CHECK_EQ(0, GetJsEntrySp()); |
403 CompileRun("js_entry_sp();"); | 431 CompileRun("js_entry_sp();"); |
404 CHECK_EQ(0, GetJsEntrySp()); | 432 CHECK_EQ(0, GetJsEntrySp()); |
405 CompileRun("js_entry_sp_level2();"); | 433 CompileRun("js_entry_sp_level2();"); |
406 CHECK_EQ(0, GetJsEntrySp()); | 434 CHECK_EQ(0, GetJsEntrySp()); |
407 } | 435 } |
408 | 436 |
409 #endif // ENABLE_LOGGING_AND_PROFILING | 437 #endif // ENABLE_LOGGING_AND_PROFILING |
OLD | NEW |