OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/trace_event/memory_dump_manager.h" | 5 #include "base/trace_event/memory_dump_manager.h" |
6 | 6 |
7 #include <inttypes.h> | 7 #include <inttypes.h> |
8 #include <stdio.h> | 8 #include <stdio.h> |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
(...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
482 MemoryDumpProvider* provider) { | 482 MemoryDumpProvider* provider) { |
483 AutoLock lock(lock_); | 483 AutoLock lock(lock_); |
484 | 484 |
485 for (const auto& info : dump_providers_) { | 485 for (const auto& info : dump_providers_) { |
486 if (info->dump_provider == provider) | 486 if (info->dump_provider == provider) |
487 return true; | 487 return true; |
488 } | 488 } |
489 return false; | 489 return false; |
490 } | 490 } |
491 | 491 |
492 scoped_refptr<base::SingleThreadTaskRunner> MemoryDumpManager::GetTaskRunner() { | |
493 lock_.AssertAcquired(); | |
494 | |
495 if (dump_thread_) | |
496 return dump_thread_->task_runner(); | |
497 | |
498 // Spin-up the thread used to invoke unbound dump providers. | |
499 std::unique_ptr<Thread> dump_thread(new Thread("MemoryInfra")); | |
500 if (!dump_thread->Start()) { | |
Primiano Tucci (use gerrit)
2017/04/24 15:30:58
I'd just violently CHECK(started) here.
Trying to
hjd
2017/04/24 16:27:59
Done.
| |
501 LOG(ERROR) << "Failed to start the memory-infra thread for tracing"; | |
502 NOTREACHED(); | |
503 } | |
504 | |
505 dump_thread_ = std::move(dump_thread); | |
506 | |
507 return dump_thread_->task_runner(); | |
508 } | |
509 | |
492 void MemoryDumpManager::CreateProcessDump( | 510 void MemoryDumpManager::CreateProcessDump( |
493 const MemoryDumpRequestArgs& args, | 511 const MemoryDumpRequestArgs& args, |
494 const ProcessMemoryDumpCallback& callback) { | 512 const ProcessMemoryDumpCallback& callback) { |
495 char guid_str[20]; | 513 char guid_str[20]; |
496 sprintf(guid_str, "0x%" PRIx64, args.dump_guid); | 514 sprintf(guid_str, "0x%" PRIx64, args.dump_guid); |
497 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(kTraceCategory, "ProcessMemoryDump", | 515 TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(kTraceCategory, "ProcessMemoryDump", |
498 TRACE_ID_LOCAL(args.dump_guid), "dump_guid", | 516 TRACE_ID_LOCAL(args.dump_guid), "dump_guid", |
499 TRACE_STR_COPY(guid_str)); | 517 TRACE_STR_COPY(guid_str)); |
500 | 518 |
501 // If argument filter is enabled then only background mode dumps should be | 519 // If argument filter is enabled then only background mode dumps should be |
502 // allowed. In case the trace config passed for background tracing session | 520 // allowed. In case the trace config passed for background tracing session |
503 // missed the allowed modes argument, it crashes here instead of creating | 521 // missed the allowed modes argument, it crashes here instead of creating |
504 // unexpected dumps. | 522 // unexpected dumps. |
505 if (TraceLog::GetInstance() | 523 if (TraceLog::GetInstance() |
506 ->GetCurrentTraceConfig() | 524 ->GetCurrentTraceConfig() |
507 .IsArgumentFilterEnabled()) { | 525 .IsArgumentFilterEnabled()) { |
508 CHECK_EQ(MemoryDumpLevelOfDetail::BACKGROUND, args.level_of_detail); | 526 CHECK_EQ(MemoryDumpLevelOfDetail::BACKGROUND, args.level_of_detail); |
509 } | 527 } |
510 | 528 |
511 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state; | 529 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state; |
512 { | 530 { |
513 AutoLock lock(lock_); | 531 AutoLock lock(lock_); |
514 | 532 |
515 // |dump_thread_| can be nullptr is tracing was disabled before reaching | |
516 // here. SetupNextMemoryDump() is robust enough to tolerate it and will | |
517 // NACK the dump. | |
518 pmd_async_state.reset(new ProcessMemoryDumpAsyncState( | 533 pmd_async_state.reset(new ProcessMemoryDumpAsyncState( |
519 args, dump_providers_, session_state_, callback, | 534 args, dump_providers_, session_state_, callback, GetTaskRunner())); |
520 dump_thread_ ? dump_thread_->task_runner() : nullptr)); | |
521 | 535 |
522 // Safety check to prevent reaching here without calling RequestGlobalDump, | 536 // Safety check to prevent reaching here without calling RequestGlobalDump, |
523 // with disallowed modes. If |session_state_| is null then tracing is | 537 // with disallowed modes. If |session_state_| is null then tracing is |
524 // disabled. | 538 // disabled. |
525 CHECK(!session_state_ || | 539 CHECK(!session_state_ || |
526 session_state_->IsDumpModeAllowed(args.level_of_detail)); | 540 session_state_->IsDumpModeAllowed(args.level_of_detail)); |
527 | 541 |
528 // If enabled, holds back the peak detector resetting its estimation window. | 542 // If enabled, holds back the peak detector resetting its estimation window. |
529 MemoryPeakDetector::GetInstance()->Throttle(); | 543 MemoryPeakDetector::GetInstance()->Throttle(); |
530 } | 544 } |
(...skipping 13 matching lines...) Expand all Loading... | |
544 // |lock_| is used in these functions purely to ensure consistency w.r.t. | 558 // |lock_| is used in these functions purely to ensure consistency w.r.t. |
545 // (un)registrations of |dump_providers_|. | 559 // (un)registrations of |dump_providers_|. |
546 void MemoryDumpManager::SetupNextMemoryDump( | 560 void MemoryDumpManager::SetupNextMemoryDump( |
547 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { | 561 std::unique_ptr<ProcessMemoryDumpAsyncState> pmd_async_state) { |
548 HEAP_PROFILER_SCOPED_IGNORE; | 562 HEAP_PROFILER_SCOPED_IGNORE; |
549 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs | 563 // Initalizes the ThreadLocalEventBuffer to guarantee that the TRACE_EVENTs |
550 // in the PostTask below don't end up registering their own dump providers | 564 // in the PostTask below don't end up registering their own dump providers |
551 // (for discounting trace memory overhead) while holding the |lock_|. | 565 // (for discounting trace memory overhead) while holding the |lock_|. |
552 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); | 566 TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported(); |
553 | 567 |
554 // |dump_thread_| might be destroyed before getting this point. | 568 // MDM might have been disabled before getting to this point. |
555 // It means that tracing was disabled right before starting this dump. | 569 // Anyway either MDM is disabled or this was the last hop, create a trace |
556 // Anyway either tracing is stopped or this was the last hop, create a trace | |
557 // event, add it to the trace and finalize process dump invoking the callback. | 570 // event, add it to the trace and finalize process dump invoking the callback. |
558 if (!pmd_async_state->dump_thread_task_runner.get()) { | 571 if (!subtle::NoBarrier_Load(&memory_tracing_enabled_)) { |
Primiano Tucci (use gerrit)
2017/04/24 15:30:58
I think this extra check is superfluous. The callb
hjd
2017/04/24 16:27:59
Removing this causes a test (MemoryDumpManagerTest
ssid
2017/04/25 03:21:47
I agree with primiano. We could get to a state whe
| |
559 if (pmd_async_state->pending_dump_providers.empty()) { | 572 if (pmd_async_state->pending_dump_providers.empty()) { |
560 VLOG(1) << kLogPrefix << " failed because dump thread was destroyed" | 573 VLOG(1) << kLogPrefix << " failed because MemoryDumpManager was disabled" |
561 << " before finalizing the dump"; | 574 << " before finalizing the dump"; |
562 } else { | 575 } else { |
563 VLOG(1) << kLogPrefix << " failed because dump thread was destroyed" | 576 VLOG(1) << kLogPrefix << " failed because MemoryDumpManager was disabled" |
564 << " before dumping " | 577 << " before dumping " |
565 << pmd_async_state->pending_dump_providers.back().get()->name; | 578 << pmd_async_state->pending_dump_providers.back().get()->name; |
566 } | 579 } |
567 pmd_async_state->dump_successful = false; | 580 pmd_async_state->dump_successful = false; |
568 pmd_async_state->pending_dump_providers.clear(); | 581 pmd_async_state->pending_dump_providers.clear(); |
569 } | 582 } |
583 | |
570 if (pmd_async_state->pending_dump_providers.empty()) | 584 if (pmd_async_state->pending_dump_providers.empty()) |
571 return FinalizeDumpAndAddToTrace(std::move(pmd_async_state)); | 585 return FinalizeDumpAndAddToTrace(std::move(pmd_async_state)); |
572 | 586 |
573 // Read MemoryDumpProviderInfo thread safety considerations in | 587 // Read MemoryDumpProviderInfo thread safety considerations in |
574 // memory_dump_manager.h when accessing |mdpinfo| fields. | 588 // memory_dump_manager.h when accessing |mdpinfo| fields. |
575 MemoryDumpProviderInfo* mdpinfo = | 589 MemoryDumpProviderInfo* mdpinfo = |
576 pmd_async_state->pending_dump_providers.back().get(); | 590 pmd_async_state->pending_dump_providers.back().get(); |
577 | 591 |
578 // If we are in background tracing, we should invoke only the whitelisted | 592 // If we are in background tracing, we should invoke only the whitelisted |
579 // providers. Ignore other providers and continue. | 593 // providers. Ignore other providers and continue. |
580 if (pmd_async_state->req_args.level_of_detail == | 594 if (pmd_async_state->req_args.level_of_detail == |
581 MemoryDumpLevelOfDetail::BACKGROUND && | 595 MemoryDumpLevelOfDetail::BACKGROUND && |
582 !mdpinfo->whitelisted_for_background_mode) { | 596 !mdpinfo->whitelisted_for_background_mode) { |
583 pmd_async_state->pending_dump_providers.pop_back(); | 597 pmd_async_state->pending_dump_providers.pop_back(); |
584 return SetupNextMemoryDump(std::move(pmd_async_state)); | 598 return SetupNextMemoryDump(std::move(pmd_async_state)); |
585 } | 599 } |
586 | 600 |
587 // If the dump provider did not specify a task runner affinity, dump on | 601 // If the dump provider did not specify a task runner affinity, dump on |
588 // |dump_thread_| which is already checked above for presence. | 602 // |dump_thread_|. |
589 SequencedTaskRunner* task_runner = mdpinfo->task_runner.get(); | 603 SequencedTaskRunner* task_runner = mdpinfo->task_runner.get(); |
590 if (!task_runner) { | 604 if (!task_runner) { |
591 DCHECK(mdpinfo->options.dumps_on_single_thread_task_runner); | 605 DCHECK(mdpinfo->options.dumps_on_single_thread_task_runner); |
592 task_runner = pmd_async_state->dump_thread_task_runner.get(); | 606 task_runner = pmd_async_state->dump_thread_task_runner.get(); |
593 DCHECK(task_runner); | 607 DCHECK(task_runner); |
594 } | 608 } |
595 | 609 |
596 if (mdpinfo->options.dumps_on_single_thread_task_runner && | 610 if (mdpinfo->options.dumps_on_single_thread_task_runner && |
597 task_runner->RunsTasksOnCurrentThread()) { | 611 task_runner->RunsTasksOnCurrentThread()) { |
598 // If |dumps_on_single_thread_task_runner| is true then no PostTask is | 612 // If |dumps_on_single_thread_task_runner| is true then no PostTask is |
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
779 pmd_async_state->callback.Run(dump_guid, dump_successful, result); | 793 pmd_async_state->callback.Run(dump_guid, dump_successful, result); |
780 pmd_async_state->callback.Reset(); | 794 pmd_async_state->callback.Reset(); |
781 } | 795 } |
782 | 796 |
783 TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", | 797 TRACE_EVENT_NESTABLE_ASYNC_END0(kTraceCategory, "ProcessMemoryDump", |
784 TRACE_ID_LOCAL(dump_guid)); | 798 TRACE_ID_LOCAL(dump_guid)); |
785 } | 799 } |
786 | 800 |
787 void MemoryDumpManager::Enable( | 801 void MemoryDumpManager::Enable( |
788 const TraceConfig::MemoryDumpConfig& memory_dump_config) { | 802 const TraceConfig::MemoryDumpConfig& memory_dump_config) { |
789 // Spin-up the thread used to invoke unbound dump providers. | |
790 std::unique_ptr<Thread> dump_thread(new Thread("MemoryInfra")); | |
791 if (!dump_thread->Start()) { | |
792 LOG(ERROR) << "Failed to start the memory-infra thread for tracing"; | |
793 return; | |
794 } | |
795 | 803 |
796 scoped_refptr<MemoryDumpSessionState> session_state = | 804 scoped_refptr<MemoryDumpSessionState> session_state = |
797 new MemoryDumpSessionState; | 805 new MemoryDumpSessionState; |
798 session_state->SetAllowedDumpModes(memory_dump_config.allowed_dump_modes); | 806 session_state->SetAllowedDumpModes(memory_dump_config.allowed_dump_modes); |
799 session_state->set_heap_profiler_breakdown_threshold_bytes( | 807 session_state->set_heap_profiler_breakdown_threshold_bytes( |
800 memory_dump_config.heap_profiler_options.breakdown_threshold_bytes); | 808 memory_dump_config.heap_profiler_options.breakdown_threshold_bytes); |
801 if (heap_profiling_enabled_) { | 809 if (heap_profiling_enabled_) { |
802 // If heap profiling is enabled, the stack frame deduplicator and type name | 810 // If heap profiling is enabled, the stack frame deduplicator and type name |
803 // deduplicator will be in use. Add a metadata events to write the frames | 811 // deduplicator will be in use. Add a metadata events to write the frames |
804 // and type IDs. | 812 // and type IDs. |
(...skipping 14 matching lines...) Expand all Loading... | |
819 "typeNames", | 827 "typeNames", |
820 MakeUnique<SessionStateConvertableProxy<TypeNameDeduplicator>>( | 828 MakeUnique<SessionStateConvertableProxy<TypeNameDeduplicator>>( |
821 session_state, &MemoryDumpSessionState::type_name_deduplicator)); | 829 session_state, &MemoryDumpSessionState::type_name_deduplicator)); |
822 } | 830 } |
823 | 831 |
824 AutoLock lock(lock_); | 832 AutoLock lock(lock_); |
825 | 833 |
826 DCHECK(delegate_); // At this point we must have a delegate. | 834 DCHECK(delegate_); // At this point we must have a delegate. |
827 session_state_ = session_state; | 835 session_state_ = session_state; |
828 | 836 |
829 DCHECK(!dump_thread_); | |
830 dump_thread_ = std::move(dump_thread); | |
831 | |
832 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); | 837 subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); |
833 | 838 |
834 MemoryDumpScheduler::Config periodic_config; | 839 MemoryDumpScheduler::Config periodic_config; |
835 bool peak_detector_configured = false; | 840 bool peak_detector_configured = false; |
836 for (const auto& trigger : memory_dump_config.triggers) { | 841 for (const auto& trigger : memory_dump_config.triggers) { |
837 if (!session_state_->IsDumpModeAllowed(trigger.level_of_detail)) { | 842 if (!session_state_->IsDumpModeAllowed(trigger.level_of_detail)) { |
838 NOTREACHED(); | 843 NOTREACHED(); |
839 continue; | 844 continue; |
840 } | 845 } |
841 if (trigger.trigger_type == MemoryDumpType::PERIODIC_INTERVAL) { | 846 if (trigger.trigger_type == MemoryDumpType::PERIODIC_INTERVAL) { |
842 if (periodic_config.triggers.empty()) { | 847 if (periodic_config.triggers.empty()) { |
843 periodic_config.callback = BindRepeating(&OnPeriodicSchedulerTick); | 848 periodic_config.callback = BindRepeating(&OnPeriodicSchedulerTick); |
844 } | 849 } |
845 periodic_config.triggers.push_back( | 850 periodic_config.triggers.push_back( |
846 {trigger.level_of_detail, trigger.min_time_between_dumps_ms}); | 851 {trigger.level_of_detail, trigger.min_time_between_dumps_ms}); |
847 } else if (trigger.trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { | 852 } else if (trigger.trigger_type == MemoryDumpType::PEAK_MEMORY_USAGE) { |
848 // At most one peak trigger is allowed. | 853 // At most one peak trigger is allowed. |
849 CHECK(!peak_detector_configured); | 854 CHECK(!peak_detector_configured); |
850 peak_detector_configured = true; | 855 peak_detector_configured = true; |
851 MemoryPeakDetector::GetInstance()->Setup( | 856 MemoryPeakDetector::GetInstance()->Setup( |
852 BindRepeating(&MemoryDumpManager::GetDumpProvidersForPolling, | 857 BindRepeating(&MemoryDumpManager::GetDumpProvidersForPolling, |
853 Unretained(this)), | 858 Unretained(this)), |
854 dump_thread_->task_runner(), | 859 GetTaskRunner(), |
855 BindRepeating(&OnPeakDetected, trigger.level_of_detail)); | 860 BindRepeating(&OnPeakDetected, trigger.level_of_detail)); |
856 | 861 |
857 MemoryPeakDetector::Config peak_config; | 862 MemoryPeakDetector::Config peak_config; |
858 peak_config.polling_interval_ms = 10; | 863 peak_config.polling_interval_ms = 10; |
859 peak_config.min_time_between_peaks_ms = trigger.min_time_between_dumps_ms; | 864 peak_config.min_time_between_peaks_ms = trigger.min_time_between_dumps_ms; |
860 peak_config.enable_verbose_poll_tracing = | 865 peak_config.enable_verbose_poll_tracing = |
861 trigger.level_of_detail == MemoryDumpLevelOfDetail::DETAILED; | 866 trigger.level_of_detail == MemoryDumpLevelOfDetail::DETAILED; |
862 MemoryPeakDetector::GetInstance()->Start(peak_config); | 867 MemoryPeakDetector::GetInstance()->Start(peak_config); |
863 | 868 |
864 // When peak detection is enabled, trigger a dump straight away as it | 869 // When peak detection is enabled, trigger a dump straight away as it |
865 // gives a good reference point for analyzing the trace. | 870 // gives a good reference point for analyzing the trace. |
866 if (delegate_->IsCoordinator()) { | 871 if (delegate_->IsCoordinator()) { |
867 dump_thread_->task_runner()->PostTask( | 872 GetTaskRunner()->PostTask( |
868 FROM_HERE, BindRepeating(&OnPeakDetected, trigger.level_of_detail)); | 873 FROM_HERE, BindRepeating(&OnPeakDetected, trigger.level_of_detail)); |
869 } | 874 } |
870 } | 875 } |
871 } | 876 } |
872 | 877 |
873 // Only coordinator process triggers periodic global memory dumps. | 878 // Only coordinator process triggers periodic global memory dumps. |
874 if (delegate_->IsCoordinator() && !periodic_config.triggers.empty()) { | 879 if (delegate_->IsCoordinator() && !periodic_config.triggers.empty()) { |
875 MemoryDumpScheduler::GetInstance()->Start(periodic_config, | 880 MemoryDumpScheduler::GetInstance()->Start(periodic_config, GetTaskRunner()); |
876 dump_thread_->task_runner()); | |
877 } | 881 } |
878 } | 882 } |
879 | 883 |
880 void MemoryDumpManager::Disable() { | 884 void MemoryDumpManager::Disable() { |
881 // There might be a memory dump in progress while this happens. Therefore, | 885 // There might be a memory dump in progress while this happens. Therefore, |
882 // ensure that the MDM state which depends on the tracing enabled / disabled | 886 // ensure that the MDM state which depends on the tracing enabled / disabled |
883 // state is always accessed by the dumping methods holding the |lock_|. | 887 // state is always accessed by the dumping methods holding the |lock_|. |
884 if (!subtle::NoBarrier_Load(&memory_tracing_enabled_)) | 888 if (!subtle::NoBarrier_Load(&memory_tracing_enabled_)) |
885 return; | 889 return; |
886 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); | 890 subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); |
887 std::unique_ptr<Thread> dump_thread; | |
888 { | 891 { |
889 AutoLock lock(lock_); | 892 AutoLock lock(lock_); |
890 MemoryDumpScheduler::GetInstance()->Stop(); | 893 MemoryDumpScheduler::GetInstance()->Stop(); |
891 MemoryPeakDetector::GetInstance()->TearDown(); | 894 MemoryPeakDetector::GetInstance()->TearDown(); |
892 dump_thread = std::move(dump_thread_); | |
893 session_state_ = nullptr; | 895 session_state_ = nullptr; |
894 } | 896 } |
895 | |
896 // Thread stops are blocking and must be performed outside of the |lock_| | |
897 // or will deadlock (e.g., if SetupNextMemoryDump() tries to acquire it). | |
898 if (dump_thread) | |
899 dump_thread->Stop(); | |
Primiano Tucci (use gerrit)
2017/04/24 15:30:58
\o/ This, as a side effect, should fix crbug.com/7
hjd
2017/04/24 16:27:59
Done.
ssid
2017/04/25 03:21:47
Yay
| |
900 } | 897 } |
901 | 898 |
902 bool MemoryDumpManager::IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) { | 899 bool MemoryDumpManager::IsDumpModeAllowed(MemoryDumpLevelOfDetail dump_mode) { |
903 AutoLock lock(lock_); | 900 AutoLock lock(lock_); |
904 if (!session_state_) | 901 if (!session_state_) |
905 return false; | 902 return false; |
906 return session_state_->IsDumpModeAllowed(dump_mode); | 903 return session_state_->IsDumpModeAllowed(dump_mode); |
907 } | 904 } |
908 | 905 |
909 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( | 906 MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState( |
(...skipping 22 matching lines...) Expand all Loading... | |
932 if (iter == process_dumps.end()) { | 929 if (iter == process_dumps.end()) { |
933 std::unique_ptr<ProcessMemoryDump> new_pmd( | 930 std::unique_ptr<ProcessMemoryDump> new_pmd( |
934 new ProcessMemoryDump(session_state, dump_args)); | 931 new ProcessMemoryDump(session_state, dump_args)); |
935 iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first; | 932 iter = process_dumps.insert(std::make_pair(pid, std::move(new_pmd))).first; |
936 } | 933 } |
937 return iter->second.get(); | 934 return iter->second.get(); |
938 } | 935 } |
939 | 936 |
940 } // namespace trace_event | 937 } // namespace trace_event |
941 } // namespace base | 938 } // namespace base |
OLD | NEW |