OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "platform/address_sanitizer.h" | 5 #include "platform/address_sanitizer.h" |
6 #include "platform/memory_sanitizer.h" | 6 #include "platform/memory_sanitizer.h" |
7 #include "platform/utils.h" | 7 #include "platform/utils.h" |
8 | 8 |
9 #include "vm/allocation.h" | 9 #include "vm/allocation.h" |
10 #include "vm/atomic.h" | 10 #include "vm/atomic.h" |
11 #include "vm/code_patcher.h" | 11 #include "vm/code_patcher.h" |
| 12 #include "vm/instructions.h" |
12 #include "vm/isolate.h" | 13 #include "vm/isolate.h" |
13 #include "vm/json_stream.h" | 14 #include "vm/json_stream.h" |
14 #include "vm/lockers.h" | 15 #include "vm/lockers.h" |
15 #include "vm/native_symbol.h" | 16 #include "vm/native_symbol.h" |
16 #include "vm/object.h" | 17 #include "vm/object.h" |
17 #include "vm/os.h" | 18 #include "vm/os.h" |
18 #include "vm/profiler.h" | 19 #include "vm/profiler.h" |
19 #include "vm/reusable_handles.h" | 20 #include "vm/reusable_handles.h" |
20 #include "vm/signal_handler.h" | 21 #include "vm/signal_handler.h" |
21 #include "vm/simulator.h" | 22 #include "vm/simulator.h" |
22 #include "vm/stack_frame.h" | 23 #include "vm/stack_frame.h" |
23 | 24 |
24 namespace dart { | 25 namespace dart { |
25 | 26 |
26 | 27 |
27 #if defined(TARGET_OS_ANDROID) || defined(HOST_ARCH_ARM64) | 28 #if defined(TARGET_OS_ANDROID) || defined(HOST_ARCH_ARM64) |
28 DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler"); | 29 DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler"); |
29 #else | 30 #else |
30 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); | 31 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); |
31 #endif | 32 #endif |
32 DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates."); | 33 DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates."); |
33 DEFINE_FLAG(bool, trace_profiler, false, "Trace profiler."); | |
34 DEFINE_FLAG(int, profile_period, 1000, | 34 DEFINE_FLAG(int, profile_period, 1000, |
35 "Time between profiler samples in microseconds. Minimum 50."); | 35 "Time between profiler samples in microseconds. Minimum 50."); |
36 DEFINE_FLAG(int, profile_depth, 8, | 36 DEFINE_FLAG(int, profile_depth, 8, |
37 "Maximum number stack frames walked. Minimum 1. Maximum 255."); | 37 "Maximum number stack frames walked. Minimum 1. Maximum 255."); |
38 #if defined(PROFILE_NATIVE_CODE) || defined(USING_SIMULATOR) | 38 #if defined(PROFILE_NATIVE_CODE) || defined(USING_SIMULATOR) |
39 DEFINE_FLAG(bool, profile_vm, true, | 39 DEFINE_FLAG(bool, profile_vm, true, |
40 "Always collect native stack traces."); | 40 "Always collect native stack traces."); |
41 #else | 41 #else |
42 DEFINE_FLAG(bool, profile_vm, false, | 42 DEFINE_FLAG(bool, profile_vm, false, |
43 "Always collect native stack traces."); | 43 "Always collect native stack traces."); |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 | 246 |
247 Sample* SampleBuffer::ReserveSample() { | 247 Sample* SampleBuffer::ReserveSample() { |
248 ASSERT(samples_ != NULL); | 248 ASSERT(samples_ != NULL); |
249 uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_); | 249 uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_); |
250 // Map back into sample buffer range. | 250 // Map back into sample buffer range. |
251 cursor = cursor % capacity_; | 251 cursor = cursor % capacity_; |
252 return At(cursor); | 252 return At(cursor); |
253 } | 253 } |
254 | 254 |
255 | 255 |
256 static void SetPCMarkerIfSafe(Sample* sample) { | 256 // Used to locate the stack slot in Dart code that contains the return address. |
257 ASSERT(sample != NULL); | 257 // NOTE: Only handles the prologue. |
258 | 258 // NOTE: Architecture specific implementations below. |
259 uword* fp = reinterpret_cast<uword*>(sample->fp()); | 259 class ReturnAddressLocator : public ValueObject { |
260 uword* sp = reinterpret_cast<uword*>(sample->sp()); | 260 public: |
261 | 261 ReturnAddressLocator(uword pc, const Code& code) |
262 // If FP == SP, the pc marker hasn't been pushed. | 262 : pc_(pc), |
263 if (fp > sp) { | 263 code_(Code::ZoneHandle(code.raw())), |
264 #if defined(TARGET_OS_WINDOWS) | 264 is_optimized_(code.is_optimized()) { |
265 // If the fp is at the beginning of a page, it may be unsafe to access | 265 ASSERT(!code_.IsNull()); |
266 // the pc marker, because we are reading it from a different thread on | 266 ASSERT(code_.ContainsInstructionAt(pc_)); |
267 // Windows. The marker is below fp and the previous page may be a guard | 267 } |
268 // page. | 268 |
269 const intptr_t kPageMask = VirtualMemory::PageSize() - 1; | 269 bool is_code_optimized() { |
270 if ((sample->fp() & kPageMask) == 0) { | 270 return is_optimized_; |
| 271 } |
| 272 |
| 273 // Returns -1 on failure. |
| 274 intptr_t ReturnAddressRelativeToSP(); |
| 275 |
| 276 // Returns offset into code object. |
| 277 uword RelativePC() { |
| 278 return pc_ - code_.EntryPoint(); |
| 279 } |
| 280 |
| 281 uint8_t* CodePointer(uword offset) { |
| 282 const uword size = code_.Size(); |
| 283 ASSERT(offset < size); |
| 284 uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code_.EntryPoint()); |
| 285 code_pointer += offset; |
| 286 return code_pointer; |
| 287 } |
| 288 |
| 289 private: |
| 290 const uword pc_; |
| 291 const Code& code_; |
| 292 const bool is_optimized_; |
| 293 }; |
| 294 |
| 295 |
| 296 #if defined(TARGET_ARCH_IA32) |
| 297 intptr_t ReturnAddressLocator::ReturnAddressRelativeToSP() { |
| 298 const uword offset = RelativePC(); |
| 299 const uword size = code_.Size(); |
| 300 if (is_optimized_) { |
| 301 // 0: push ebp |
| 302 // 1: mov ebp, esp |
| 303 // 3: ... |
| 304 if (offset == 0x0) { |
| 305 // Stack layout: |
| 306 // 0 RETURN ADDRESS. |
| 307 return 0; |
| 308 } |
| 309 if (offset == 0x1) { |
| 310 // Stack layout: |
| 311 // 0 CALLER FRAME POINTER |
| 312 // 1 RETURN ADDRESS |
| 313 return 1; |
| 314 } |
| 315 return -1; |
| 316 } else { |
| 317 // 0x00: mov edi, function |
| 318 // 0x05: incl (inc usage count) <-- this is optional. |
| 319 // 0x08: cmpl (compare usage count) |
| 320 // 0x0f: jump to optimize function |
| 321 // 0x15: push ebp |
| 322 // 0x16: mov ebp, esp |
| 323 // 0x18: ... |
| 324 ASSERT(size >= 0x08); |
| 325 const uword incl_offset = 0x05; |
| 326 const uword incl_length = 0x03; |
| 327 const uint8_t incl_op_code = 0xFF; |
| 328 const bool has_incl = (*CodePointer(incl_offset) == incl_op_code); |
| 329 const uword push_fp_offset = has_incl ? 0x15 : 0x15 - incl_length; |
| 330 if (offset <= push_fp_offset) { |
| 331 // Stack layout: |
| 332 // 0 RETURN ADDRESS. |
| 333 return 0; |
| 334 } |
| 335 if (offset == (push_fp_offset + 1)) { |
| 336 // Stack layout: |
| 337 // 0 CALLER FRAME POINTER |
| 338 // 1 RETURN ADDRESS |
| 339 return 1; |
| 340 } |
| 341 return -1; |
| 342 } |
| 343 UNREACHABLE(); |
| 344 return -1; |
| 345 } |
| 346 #elif defined(TARGET_ARCH_X64) |
| 347 intptr_t ReturnAddressLocator::ReturnAddressRelativeToSP() { |
| 348 const uword offset = RelativePC(); |
| 349 const uword size = code_.Size(); |
| 350 if (is_optimized_) { |
| 351 // 0x00: leaq (load pc marker) |
| 352 // 0x07: movq (load pool pointer) |
| 353 // 0x0c: push rpb |
| 354 // 0x0d: movq rbp, rsp |
| 355 // 0x10: ... |
| 356 const uword push_fp_offset = 0x0c; |
| 357 if (offset <= push_fp_offset) { |
| 358 // Stack layout: |
| 359 // 0 RETURN ADDRESS. |
| 360 return 0; |
| 361 } |
| 362 if (offset == (push_fp_offset + 1)) { |
| 363 // Stack layout: |
| 364 // 0 CALLER FRAME POINTER |
| 365 // 1 RETURN ADDRESS |
| 366 return 1; |
| 367 } |
| 368 return -1; |
| 369 } else { |
| 370 // 0x00: leaq (load pc marker) |
| 371 // 0x07: movq (load pool pointer) |
| 372 // 0x0c: movq (load function) |
| 373 // 0x13: incl (inc usage count) <-- this is optional. |
| 374 // 0x16: cmpl (compare usage count) |
| 375 // 0x1d: jl + 0x |
| 376 // 0x23: jmp [pool pointer] |
| 377 // 0x27: push rbp |
| 378 // 0x28: movq rbp, rsp |
| 379 // 0x2b: ... |
| 380 ASSERT(size >= 0x16); |
| 381 const uword incl_offset = 0x13; |
| 382 const uword incl_length = 0x03; |
| 383 const uint8_t incl_op_code = 0xFF; |
| 384 const bool has_incl = (*CodePointer(incl_offset) == incl_op_code); |
| 385 const uword push_fp_offset = has_incl ? 0x27 : 0x27 - incl_length; |
| 386 if (offset <= push_fp_offset) { |
| 387 // Stack layout: |
| 388 // 0 RETURN ADDRESS. |
| 389 return 0; |
| 390 } |
| 391 if (offset == (push_fp_offset + 1)) { |
| 392 // Stack layout: |
| 393 // 0 CALLER FRAME POINTER |
| 394 // 1 RETURN ADDRESS |
| 395 return 1; |
| 396 } |
| 397 return -1; |
| 398 } |
| 399 UNREACHABLE(); |
| 400 return -1; |
| 401 } |
| 402 #elif defined(TARGET_ARCH_ARM) |
| 403 intptr_t ReturnAddressLocator::ReturnAddressRelativeToSP() { |
| 404 return -1; |
| 405 } |
| 406 #elif defined(TARGET_ARCH_ARM64) |
| 407 intptr_t ReturnAddressLocator::ReturnAddressRelativeToSP() { |
| 408 return -1; |
| 409 } |
| 410 #elif defined(TARGET_ARCH_MIPS) |
| 411 intptr_t ReturnAddressLocator::ReturnAddressRelativeToSP() { |
| 412 return -1; |
| 413 } |
| 414 #else |
| 415 #error ReturnAddressLocator implementation missing for this architecture. |
| 416 #endif |
| 417 |
| 418 |
| 419 FixTopFrameVisitor::FixTopFrameVisitor(Isolate* isolate) |
| 420 : SampleVisitor(isolate), |
| 421 vm_isolate_(Dart::vm_isolate()) { |
| 422 } |
| 423 |
| 424 |
| 425 void FixTopFrameVisitor::VisitSample(Sample* sample) { |
| 426 if (sample->processed()) { |
| 427 // Already processed. |
| 428 return; |
| 429 } |
| 430 REUSABLE_CODE_HANDLESCOPE(isolate()); |
| 431 // Mark that we've processed this sample. |
| 432 sample->set_processed(true); |
| 433 // Lookup code object for leaf frame. |
| 434 Code& code = reused_code_handle.Handle(); |
| 435 code = FindCodeForPC(sample->At(0)); |
| 436 sample->set_leaf_frame_is_dart(!code.IsNull()); |
| 437 if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) { |
| 438 // Code compiled after sample. Ignore. |
| 439 return; |
| 440 } |
| 441 if (sample->leaf_frame_is_dart()) { |
| 442 CheckForMissingDartFrame(code, sample); |
| 443 } |
| 444 } |
| 445 |
| 446 |
| 447 void FixTopFrameVisitor::CheckForMissingDartFrame(const Code& code, |
| 448 Sample* sample) const { |
| 449 // Some stubs (and intrinsics) do not push a frame onto the stack leaving |
| 450 // the frame pointer in the caller. |
| 451 // |
| 452 // PC -> STUB |
| 453 // FP -> DART3 <-+ |
| 454 // DART2 <-| <- TOP FRAME RETURN ADDRESS. |
| 455 // DART1 <-| |
| 456 // ..... |
| 457 // |
| 458 // In this case, traversing the linked stack frames will not collect a PC |
| 459 // inside DART3. The stack will incorrectly be: STUB, DART2, DART1. |
| 460 // In Dart code, after pushing the FP onto the stack, an IP in the current |
| 461 // function is pushed onto the stack as well. This stack slot is called |
| 462 // the PC marker. We can use the PC marker to insert DART3 into the stack |
| 463 // so that it will correctly be: STUB, DART3, DART2, DART1. Note the |
| 464 // inserted PC may not accurately reflect the true return address into DART3. |
| 465 ASSERT(!code.IsNull()); |
| 466 |
| 467 // The pc marker is our current best guess of a return address. |
| 468 uword return_address = sample->pc_marker(); |
| 469 |
| 470 // Attempt to find a better return address. |
| 471 ReturnAddressLocator ral(sample->At(0), code); |
| 472 ReturnPattern rp(sample->At(0)); |
| 473 |
| 474 intptr_t return_address_slot = ral.ReturnAddressRelativeToSP(); |
| 475 if (return_address_slot != -1) { |
| 476 // In prologue, located the return address. |
| 477 ASSERT(return_address_slot >= 0); |
| 478 ASSERT(return_address_slot < Sample::kStackBufferSizeInWords); |
| 479 return_address = sample->GetStackBuffer()[return_address_slot]; |
| 480 } else if (rp.IsValid()) { |
| 481 // In epilogue, located the return address. |
| 482 return_address = sample->possible_return_address(); |
| 483 } else { |
| 484 // Could not find a better return address than the pc_marker. |
| 485 if (code.ContainsInstructionAt(return_address)) { |
| 486 // PC marker is in the same code as pc, no missing frame. |
271 return; | 487 return; |
272 } | 488 } |
273 #endif | 489 } |
274 uword* pc_marker_ptr = fp + kPcMarkerSlotFromFp; | 490 |
275 // MSan/ASan are unaware of frames initialized by generated code. | 491 if (return_address != 0) { |
276 MSAN_UNPOISON(pc_marker_ptr, kWordSize); | 492 sample->InsertCallerForTopFrame(return_address); |
277 ASAN_UNPOISON(pc_marker_ptr, kWordSize); | 493 } |
278 sample->set_pc_marker(*pc_marker_ptr); | 494 } |
279 } | 495 |
| 496 |
| 497 bool FixTopFrameVisitor::ContainedInDartCodeHeaps(uword pc) const { |
| 498 return isolate()->heap()->CodeContains(pc) || |
| 499 vm_isolate()->heap()->CodeContains(pc); |
| 500 } |
| 501 |
| 502 |
| 503 RawCode* FixTopFrameVisitor::FindCodeForPC(uword pc) const { |
| 504 // Check current isolate for pc. |
| 505 if (isolate()->heap()->CodeContains(pc)) { |
| 506 return Code::LookupCode(pc); |
| 507 } |
| 508 // Check VM isolate for pc. |
| 509 if (vm_isolate()->heap()->CodeContains(pc)) { |
| 510 return Code::LookupCodeInVmIsolate(pc); |
| 511 } |
| 512 return Code::null(); |
280 } | 513 } |
281 | 514 |
282 | 515 |
283 // Given an exit frame, walk the Dart stack. | 516 // Given an exit frame, walk the Dart stack. |
284 class ProfilerDartExitStackWalker : public ValueObject { | 517 class ProfilerDartExitStackWalker : public ValueObject { |
285 public: | 518 public: |
286 ProfilerDartExitStackWalker(Isolate* isolate, Sample* sample) | 519 ProfilerDartExitStackWalker(Isolate* isolate, Sample* sample) |
287 : sample_(sample), | 520 : sample_(sample), |
288 frame_iterator_(isolate) { | 521 frame_iterator_(isolate) { |
289 ASSERT(sample_ != NULL); | 522 ASSERT(sample_ != NULL); |
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 | 801 |
569 Sample* sample_; | 802 Sample* sample_; |
570 const uword stack_upper_; | 803 const uword stack_upper_; |
571 const uword original_pc_; | 804 const uword original_pc_; |
572 const uword original_fp_; | 805 const uword original_fp_; |
573 const uword original_sp_; | 806 const uword original_sp_; |
574 uword lower_bound_; | 807 uword lower_bound_; |
575 }; | 808 }; |
576 | 809 |
577 | 810 |
| 811 static void SetPCMarkerIfSafe(Sample* sample) { |
| 812 ASSERT(sample != NULL); |
| 813 |
| 814 uword* fp = reinterpret_cast<uword*>(sample->fp()); |
| 815 uword* sp = reinterpret_cast<uword*>(sample->sp()); |
| 816 |
| 817 // If FP == SP, the pc marker hasn't been pushed. |
| 818 if (fp > sp) { |
| 819 #if defined(TARGET_OS_WINDOWS) |
| 820 // If the fp is at the beginning of a page, it may be unsafe to access |
| 821 // the pc marker, because we are reading it from a different thread on |
| 822 // Windows. The marker is below fp and the previous page may be a guard |
| 823 // page. |
| 824 const intptr_t kPageMask = VirtualMemory::PageSize() - 1; |
| 825 if ((sample->fp() & kPageMask) == 0) { |
| 826 return; |
| 827 } |
| 828 #endif |
| 829 uword* pc_marker_ptr = fp + kPcMarkerSlotFromFp; |
| 830 // MSan/ASan are unaware of frames initialized by generated code. |
| 831 MSAN_UNPOISON(pc_marker_ptr, kWordSize); |
| 832 ASAN_UNPOISON(pc_marker_ptr, kWordSize); |
| 833 sample->set_pc_marker(*pc_marker_ptr); |
| 834 } |
| 835 } |
| 836 |
| 837 |
| 838 static void GrabPossibleReturnValue(Sample* sample) { |
| 839 ASSERT(sample != NULL); |
| 840 // On other architectures possible_return_address is filled in from a |
| 841 // register that was captured by the signal handler. |
| 842 #if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32) || \ |
| 843 defined(USING_SIMULATOR) |
| 844 uword* sp = reinterpret_cast<uword*>(sample->sp()); |
| 845 if (sp != NULL) { |
| 846 sample->set_possible_return_address(*sp); |
| 847 } |
| 848 #endif |
| 849 } |
| 850 |
| 851 |
| 852 static void FillStackBuffer(Sample* sample) { |
| 853 ASSERT(sample != NULL); |
| 854 uword* sp = reinterpret_cast<uword*>(sample->sp()); |
| 855 uword* stack_buffer = sample->GetStackBuffer(); |
| 856 if (sp != NULL) { |
| 857 for (intptr_t i = 0; i < Sample::kStackBufferSizeInWords; i++) { |
| 858 stack_buffer[i] = *sp; |
| 859 sp++; |
| 860 } |
| 861 } |
| 862 } |
| 863 |
| 864 |
578 void Profiler::RecordSampleInterruptCallback( | 865 void Profiler::RecordSampleInterruptCallback( |
579 const InterruptedThreadState& state, | 866 const InterruptedThreadState& state, |
580 void* data) { | 867 void* data) { |
581 Isolate* isolate = reinterpret_cast<Isolate*>(data); | 868 Isolate* isolate = reinterpret_cast<Isolate*>(data); |
582 if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) { | 869 if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) { |
583 // No isolate. | 870 // No isolate. |
584 return; | 871 return; |
585 } | 872 } |
586 | 873 |
587 ASSERT(isolate != Dart::vm_isolate()); | 874 ASSERT(isolate != Dart::vm_isolate()); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
672 } | 959 } |
673 #endif | 960 #endif |
674 // Increment counter for vm tag. | 961 // Increment counter for vm tag. |
675 VMTagCounters* counters = isolate->vm_tag_counters(); | 962 VMTagCounters* counters = isolate->vm_tag_counters(); |
676 ASSERT(counters != NULL); | 963 ASSERT(counters != NULL); |
677 counters->Increment(vm_tag); | 964 counters->Increment(vm_tag); |
678 sample->set_vm_tag(vm_tag); | 965 sample->set_vm_tag(vm_tag); |
679 sample->set_user_tag(isolate->user_tag()); | 966 sample->set_user_tag(isolate->user_tag()); |
680 sample->set_sp(sp); | 967 sample->set_sp(sp); |
681 sample->set_fp(state.fp); | 968 sample->set_fp(state.fp); |
| 969 sample->set_possible_return_address(state.ra); |
| 970 GrabPossibleReturnValue(sample); |
| 971 FillStackBuffer(sample); |
682 #if !(defined(TARGET_OS_WINDOWS) && defined(TARGET_ARCH_X64)) | 972 #if !(defined(TARGET_OS_WINDOWS) && defined(TARGET_ARCH_X64)) |
683 // It is never safe to read other thread's stack unless on Win64 | 973 // It is never safe to read other thread's stack unless on Win64 |
684 // other thread is inside Dart code. | 974 // other thread is inside Dart code. |
685 SetPCMarkerIfSafe(sample); | 975 SetPCMarkerIfSafe(sample); |
686 #endif | 976 #endif |
687 | 977 |
688 // Walk the call stack. | 978 // Walk the call stack. |
689 if (FLAG_profile_vm) { | 979 if (FLAG_profile_vm) { |
690 // Always walk the native stack collecting both native and Dart frames. | 980 // Always walk the native stack collecting both native and Dart frames. |
691 ProfilerNativeStackWalker stackWalker(sample, | 981 ProfilerNativeStackWalker stackWalker(sample, |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
729 state.pc, | 1019 state.pc, |
730 state.fp, | 1020 state.fp, |
731 sp); | 1021 sp); |
732 stackWalker.walk(); | 1022 stackWalker.walk(); |
733 #endif | 1023 #endif |
734 } | 1024 } |
735 } | 1025 } |
736 } | 1026 } |
737 | 1027 |
738 } // namespace dart | 1028 } // namespace dart |
OLD | NEW |