OLD | NEW |
1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 static TraceExtension kTraceExtension; | 180 static TraceExtension kTraceExtension; |
181 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); | 181 v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |
182 | 182 |
183 | 183 |
184 static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) { | 184 static bool IsAddressWithinFuncCode(JSFunction* function, Address addr) { |
185 i::Code* code = function->code(); | 185 i::Code* code = function->code(); |
186 return code->contains(addr); | 186 return code->contains(addr); |
187 } | 187 } |
188 | 188 |
189 | 189 |
190 static bool IsAddressWithinFuncCode(const char* func_name, Address addr) { | 190 static bool IsAddressWithinFuncCode(v8::Local<v8::Context> context, |
191 v8::Local<v8::Value> func = CcTest::env()->Global()->Get(v8_str(func_name)); | 191 const char* func_name, |
| 192 Address addr) { |
| 193 v8::Local<v8::Value> func = context->Global()->Get(v8_str(func_name)); |
192 CHECK(func->IsFunction()); | 194 CHECK(func->IsFunction()); |
193 JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func)); | 195 JSFunction* js_func = JSFunction::cast(*v8::Utils::OpenHandle(*func)); |
194 return IsAddressWithinFuncCode(js_func, addr); | 196 return IsAddressWithinFuncCode(js_func, addr); |
195 } | 197 } |
196 | 198 |
197 | 199 |
198 // This C++ function is called as a constructor, to grab the frame pointer | 200 // This C++ function is called as a constructor, to grab the frame pointer |
199 // from the calling function. When this function runs, the stack contains | 201 // from the calling function. When this function runs, the stack contains |
200 // a C_Entry frame and a Construct frame above the calling function's frame. | 202 // a C_Entry frame and a Construct frame above the calling function's frame. |
201 static void construct_call(const v8::FunctionCallbackInfo<v8::Value>& args) { | 203 static void construct_call(const v8::FunctionCallbackInfo<v8::Value>& args) { |
(...skipping 16 matching lines...) Expand all Loading... |
218 args.This()->Set(v8_str("low_bits"), v8_num(low_bits)); | 220 args.This()->Set(v8_str("low_bits"), v8_num(low_bits)); |
219 args.This()->Set(v8_str("high_bits"), v8_num(high_bits)); | 221 args.This()->Set(v8_str("high_bits"), v8_num(high_bits)); |
220 #else | 222 #else |
221 #error Host architecture is neither 32-bit nor 64-bit. | 223 #error Host architecture is neither 32-bit nor 64-bit. |
222 #endif | 224 #endif |
223 args.GetReturnValue().Set(args.This()); | 225 args.GetReturnValue().Set(args.This()); |
224 } | 226 } |
225 | 227 |
226 | 228 |
227 // Use the API to create a JSFunction object that calls the above C++ function. | 229 // Use the API to create a JSFunction object that calls the above C++ function. |
228 void CreateFramePointerGrabberConstructor(const char* constructor_name) { | 230 void CreateFramePointerGrabberConstructor(v8::Local<v8::Context> context, |
| 231 const char* constructor_name) { |
229 Local<v8::FunctionTemplate> constructor_template = | 232 Local<v8::FunctionTemplate> constructor_template = |
230 v8::FunctionTemplate::New(construct_call); | 233 v8::FunctionTemplate::New(construct_call); |
231 constructor_template->SetClassName(v8_str("FPGrabber")); | 234 constructor_template->SetClassName(v8_str("FPGrabber")); |
232 Local<Function> fun = constructor_template->GetFunction(); | 235 Local<Function> fun = constructor_template->GetFunction(); |
233 CcTest::env()->Global()->Set(v8_str(constructor_name), fun); | 236 context->Global()->Set(v8_str(constructor_name), fun); |
234 } | 237 } |
235 | 238 |
236 | 239 |
237 // Creates a global function named 'func_name' that calls the tracing | 240 // Creates a global function named 'func_name' that calls the tracing |
238 // function 'trace_func_name' with an actual EBP register value, | 241 // function 'trace_func_name' with an actual EBP register value, |
239 // encoded as one or two Smis. | 242 // encoded as one or two Smis. |
240 static void CreateTraceCallerFunction(const char* func_name, | 243 static void CreateTraceCallerFunction(v8::Local<v8::Context> context, |
| 244 const char* func_name, |
241 const char* trace_func_name) { | 245 const char* trace_func_name) { |
242 i::EmbeddedVector<char, 256> trace_call_buf; | 246 i::EmbeddedVector<char, 256> trace_call_buf; |
243 i::OS::SNPrintF(trace_call_buf, | 247 i::OS::SNPrintF(trace_call_buf, |
244 "function %s() {" | 248 "function %s() {" |
245 " fp = new FPGrabber();" | 249 " fp = new FPGrabber();" |
246 " %s(fp.low_bits, fp.high_bits);" | 250 " %s(fp.low_bits, fp.high_bits);" |
247 "}", | 251 "}", |
248 func_name, trace_func_name); | 252 func_name, trace_func_name); |
249 | 253 |
250 // Create the FPGrabber function, which grabs the caller's frame pointer | 254 // Create the FPGrabber function, which grabs the caller's frame pointer |
251 // when called as a constructor. | 255 // when called as a constructor. |
252 CreateFramePointerGrabberConstructor("FPGrabber"); | 256 CreateFramePointerGrabberConstructor(context, "FPGrabber"); |
253 | 257 |
254 // Compile the script. | 258 // Compile the script. |
255 CompileRun(trace_call_buf.start()); | 259 CompileRun(trace_call_buf.start()); |
256 } | 260 } |
257 | 261 |
258 | 262 |
259 // This test verifies that stack tracing works when called during | 263 // This test verifies that stack tracing works when called during |
260 // execution of a native function called from JS code. In this case, | 264 // execution of a native function called from JS code. In this case, |
261 // TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack | 265 // TickSample::Trace uses Isolate::c_entry_fp as a starting point for stack |
262 // walking. | 266 // walking. |
263 TEST(CFromJSStackTrace) { | 267 TEST(CFromJSStackTrace) { |
264 // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test. | 268 // BUG(1303) Inlining of JSFuncDoTrace() in JSTrace below breaks this test. |
265 i::FLAG_use_inlining = false; | 269 i::FLAG_use_inlining = false; |
266 | 270 |
267 TickSample sample; | 271 TickSample sample; |
268 InitTraceEnv(&sample); | 272 InitTraceEnv(&sample); |
269 | 273 |
270 CcTest::InitializeVM(TRACE_EXTENSION); | |
271 v8::HandleScope scope(CcTest::isolate()); | 274 v8::HandleScope scope(CcTest::isolate()); |
| 275 v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION); |
| 276 v8::Context::Scope context_scope(context); |
| 277 |
272 // Create global function JSFuncDoTrace which calls | 278 // Create global function JSFuncDoTrace which calls |
273 // extension function trace() with the current frame pointer value. | 279 // extension function trace() with the current frame pointer value. |
274 CreateTraceCallerFunction("JSFuncDoTrace", "trace"); | 280 CreateTraceCallerFunction(context, "JSFuncDoTrace", "trace"); |
275 Local<Value> result = CompileRun( | 281 Local<Value> result = CompileRun( |
276 "function JSTrace() {" | 282 "function JSTrace() {" |
277 " JSFuncDoTrace();" | 283 " JSFuncDoTrace();" |
278 "};\n" | 284 "};\n" |
279 "JSTrace();\n" | 285 "JSTrace();\n" |
280 "true;"); | 286 "true;"); |
281 CHECK(!result.IsEmpty()); | 287 CHECK(!result.IsEmpty()); |
282 // When stack tracer is invoked, the stack should look as follows: | 288 // When stack tracer is invoked, the stack should look as follows: |
283 // script [JS] | 289 // script [JS] |
284 // JSTrace() [JS] | 290 // JSTrace() [JS] |
285 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] | 291 // JSFuncDoTrace() [JS] [captures EBP value and encodes it as Smi] |
286 // trace(EBP) [native (extension)] | 292 // trace(EBP) [native (extension)] |
287 // DoTrace(EBP) [native] | 293 // DoTrace(EBP) [native] |
288 // TickSample::Trace | 294 // TickSample::Trace |
289 | 295 |
290 CHECK(sample.has_external_callback); | 296 CHECK(sample.has_external_callback); |
291 CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback); | 297 CHECK_EQ(FUNCTION_ADDR(TraceExtension::Trace), sample.external_callback); |
292 | 298 |
293 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" | 299 // Stack tracing will start from the first JS function, i.e. "JSFuncDoTrace" |
294 int base = 0; | 300 int base = 0; |
295 CHECK_GT(sample.frames_count, base + 1); | 301 CHECK_GT(sample.frames_count, base + 1); |
296 | 302 |
297 CHECK(IsAddressWithinFuncCode("JSFuncDoTrace", sample.stack[base + 0])); | 303 CHECK(IsAddressWithinFuncCode( |
298 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 1])); | 304 context, "JSFuncDoTrace", sample.stack[base + 0])); |
| 305 CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 1])); |
299 } | 306 } |
300 | 307 |
301 | 308 |
302 // This test verifies that stack tracing works when called during | 309 // This test verifies that stack tracing works when called during |
303 // execution of JS code. However, as calling TickSample::Trace requires | 310 // execution of JS code. However, as calling TickSample::Trace requires |
304 // entering native code, we can only emulate pure JS by erasing | 311 // entering native code, we can only emulate pure JS by erasing |
305 // Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame | 312 // Isolate::c_entry_fp value. In this case, TickSample::Trace uses passed frame |
306 // pointer value as a starting point for stack walking. | 313 // pointer value as a starting point for stack walking. |
307 TEST(PureJSStackTrace) { | 314 TEST(PureJSStackTrace) { |
308 // This test does not pass with inlining enabled since inlined functions | 315 // This test does not pass with inlining enabled since inlined functions |
309 // don't appear in the stack trace. | 316 // don't appear in the stack trace. |
310 i::FLAG_use_inlining = false; | 317 i::FLAG_use_inlining = false; |
311 | 318 |
312 TickSample sample; | 319 TickSample sample; |
313 InitTraceEnv(&sample); | 320 InitTraceEnv(&sample); |
314 | 321 |
315 CcTest::InitializeVM(TRACE_EXTENSION); | |
316 v8::HandleScope scope(CcTest::isolate()); | 322 v8::HandleScope scope(CcTest::isolate()); |
| 323 v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION); |
| 324 v8::Context::Scope context_scope(context); |
| 325 |
317 // Create global function JSFuncDoTrace which calls | 326 // Create global function JSFuncDoTrace which calls |
318 // extension function js_trace() with the current frame pointer value. | 327 // extension function js_trace() with the current frame pointer value. |
319 CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); | 328 CreateTraceCallerFunction(context, "JSFuncDoTrace", "js_trace"); |
320 Local<Value> result = CompileRun( | 329 Local<Value> result = CompileRun( |
321 "function JSTrace() {" | 330 "function JSTrace() {" |
322 " JSFuncDoTrace();" | 331 " JSFuncDoTrace();" |
323 "};\n" | 332 "};\n" |
324 "function OuterJSTrace() {" | 333 "function OuterJSTrace() {" |
325 " JSTrace();" | 334 " JSTrace();" |
326 "};\n" | 335 "};\n" |
327 "OuterJSTrace();\n" | 336 "OuterJSTrace();\n" |
328 "true;"); | 337 "true;"); |
329 CHECK(!result.IsEmpty()); | 338 CHECK(!result.IsEmpty()); |
330 // When stack tracer is invoked, the stack should look as follows: | 339 // When stack tracer is invoked, the stack should look as follows: |
331 // script [JS] | 340 // script [JS] |
332 // OuterJSTrace() [JS] | 341 // OuterJSTrace() [JS] |
333 // JSTrace() [JS] | 342 // JSTrace() [JS] |
334 // JSFuncDoTrace() [JS] | 343 // JSFuncDoTrace() [JS] |
335 // js_trace(EBP) [native (extension)] | 344 // js_trace(EBP) [native (extension)] |
336 // DoTraceHideCEntryFPAddress(EBP) [native] | 345 // DoTraceHideCEntryFPAddress(EBP) [native] |
337 // TickSample::Trace | 346 // TickSample::Trace |
338 // | 347 // |
339 | 348 |
340 CHECK(sample.has_external_callback); | 349 CHECK(sample.has_external_callback); |
341 CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback); | 350 CHECK_EQ(FUNCTION_ADDR(TraceExtension::JSTrace), sample.external_callback); |
342 | 351 |
343 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" | 352 // Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace" |
344 int base = 0; | 353 int base = 0; |
345 CHECK_GT(sample.frames_count, base + 1); | 354 CHECK_GT(sample.frames_count, base + 1); |
346 CHECK(IsAddressWithinFuncCode("JSTrace", sample.stack[base + 0])); | 355 CHECK(IsAddressWithinFuncCode(context, "JSTrace", sample.stack[base + 0])); |
347 CHECK(IsAddressWithinFuncCode("OuterJSTrace", sample.stack[base + 1])); | 356 CHECK(IsAddressWithinFuncCode( |
| 357 context, "OuterJSTrace", sample.stack[base + 1])); |
348 } | 358 } |
349 | 359 |
350 | 360 |
351 static void CFuncDoTrace(byte dummy_parameter) { | 361 static void CFuncDoTrace(byte dummy_parameter) { |
352 Address fp; | 362 Address fp; |
353 #ifdef __GNUC__ | 363 #ifdef __GNUC__ |
354 fp = reinterpret_cast<Address>(__builtin_frame_address(0)); | 364 fp = reinterpret_cast<Address>(__builtin_frame_address(0)); |
355 #elif defined _MSC_VER | 365 #elif defined _MSC_VER |
356 // Approximate a frame pointer address. We compile without base pointers, | 366 // Approximate a frame pointer address. We compile without base pointers, |
357 // so we can't trust ebp/rbp. | 367 // so we can't trust ebp/rbp. |
(...skipping 14 matching lines...) Expand all Loading... |
372 } | 382 } |
373 } | 383 } |
374 | 384 |
375 | 385 |
376 // This test verifies that stack tracing doesn't crash when called on | 386 // This test verifies that stack tracing doesn't crash when called on |
377 // pure native code. TickSample::Trace only unrolls JS code, so we can't | 387 // pure native code. TickSample::Trace only unrolls JS code, so we can't |
378 // get any meaningful info here. | 388 // get any meaningful info here. |
379 TEST(PureCStackTrace) { | 389 TEST(PureCStackTrace) { |
380 TickSample sample; | 390 TickSample sample; |
381 InitTraceEnv(&sample); | 391 InitTraceEnv(&sample); |
382 CcTest::InitializeVM(TRACE_EXTENSION); | 392 v8::HandleScope scope(CcTest::isolate()); |
| 393 v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION); |
| 394 v8::Context::Scope context_scope(context); |
383 // Check that sampler doesn't crash | 395 // Check that sampler doesn't crash |
384 CHECK_EQ(10, CFunc(10)); | 396 CHECK_EQ(10, CFunc(10)); |
385 } | 397 } |
386 | 398 |
387 | 399 |
388 TEST(JsEntrySp) { | 400 TEST(JsEntrySp) { |
389 CcTest::InitializeVM(TRACE_EXTENSION); | |
390 v8::HandleScope scope(CcTest::isolate()); | 401 v8::HandleScope scope(CcTest::isolate()); |
| 402 v8::Local<v8::Context> context = CcTest::NewContext(TRACE_EXTENSION); |
| 403 v8::Context::Scope context_scope(context); |
391 CHECK_EQ(0, GetJsEntrySp()); | 404 CHECK_EQ(0, GetJsEntrySp()); |
392 CompileRun("a = 1; b = a + 1;"); | 405 CompileRun("a = 1; b = a + 1;"); |
393 CHECK_EQ(0, GetJsEntrySp()); | 406 CHECK_EQ(0, GetJsEntrySp()); |
394 CompileRun("js_entry_sp();"); | 407 CompileRun("js_entry_sp();"); |
395 CHECK_EQ(0, GetJsEntrySp()); | 408 CHECK_EQ(0, GetJsEntrySp()); |
396 CompileRun("js_entry_sp_level2();"); | 409 CompileRun("js_entry_sp_level2();"); |
397 CHECK_EQ(0, GetJsEntrySp()); | 410 CHECK_EQ(0, GetJsEntrySp()); |
398 } | 411 } |
OLD | NEW |