| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/memory/oom_kills_monitor.h" |
| |
| #include "base/logging.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/notreached.h" |
| #include "base/process/process_metrics.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/metrics/daily_event.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| namespace memory { |
| |
| const char OOMKillsMonitor::kOOMKillsCountHistogramName[] = |
| "Memory.OOMKills.Count"; |
| |
| const char OOMKillsMonitor::kOOMKillsDailyHistogramName[] = |
| "Memory.OOMKills.Daily"; |
| |
| namespace { |
| |
| void UmaHistogramOOMKills(int oom_kills) { |
| base::UmaHistogramCustomCounts(OOMKillsMonitor::kOOMKillsCountHistogramName, |
| oom_kills, 1, 1000, 1001); |
| } |
| |
| // The interval at which the DailyEvent::CheckInterval function should be |
| // called. |
| constexpr base::TimeDelta kDailyEventIntervalTimeDelta = base::Minutes(30); |
| |
| // The name of the histogram used to report that the OOM Kills daily event |
| // happened. |
| const char kOOMKillsDailyEventHistogramName[] = |
| "Memory.OOMKills.DailyEventInterval"; |
| |
| } // namespace |
| |
| // This shim class is needed since metrics::DailyEvent requires taking ownership |
| // of its observers. It just forwards events to OOMKillsMonitor. |
| class OOMKillsMonitor::DailyEventObserver |
| : public ::metrics::DailyEvent::Observer { |
| public: |
| explicit DailyEventObserver(OOMKillsMonitor* reporter) |
| : reporter_(reporter) {} |
| |
| DailyEventObserver(const DailyEventObserver&) = delete; |
| DailyEventObserver& operator=(const DailyEventObserver&) = delete; |
| |
| ~DailyEventObserver() override = default; |
| |
| void OnDailyEvent(::metrics::DailyEvent::IntervalType type) override { |
| reporter_->ReportDailyMetrics(type); |
| } |
| |
| private: |
| raw_ptr<OOMKillsMonitor> reporter_; |
| }; |
| |
| OOMKillsMonitor::OOMKillsMonitor() = default; |
| |
| OOMKillsMonitor::~OOMKillsMonitor() = default; |
| |
| // static |
| OOMKillsMonitor& OOMKillsMonitor::GetInstance() { |
| static base::NoDestructor<OOMKillsMonitor> instance; |
| return *instance; |
| } |
| |
| void OOMKillsMonitor::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterIntegerPref(::prefs::kOOMKillsDailyCount, 0); |
| ::metrics::DailyEvent::RegisterPref(registry, ::prefs::kOOMKillsDailySample); |
| } |
| |
| void OOMKillsMonitor::Initialize(PrefService* pref_service) { |
| VLOG(2) << "Starting OOM kills monitor from thread " |
| << base::PlatformThread::CurrentId(); |
| |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (monitoring_started_) { |
| NOTREACHED() << "OOM kiils monitor should only be initialized once"; |
| } |
| |
| monitoring_started_ = true; |
| |
| // Insert a zero kill record at the beginning for easy comparison to those |
| // with non-zero kill sessions. |
| UmaHistogramOOMKills(0); |
| |
| base::VmStatInfo vmstat; |
| if (base::GetVmStatInfo(&vmstat)) { |
| last_oom_kills_count_ = vmstat.oom_kill; |
| } else { |
| last_oom_kills_count_ = 0; |
| } |
| |
| checking_timer_.Start(FROM_HERE, base::Minutes(1), |
| base::BindRepeating(&OOMKillsMonitor::CheckOOMKill, |
| base::Unretained(this))); |
| |
| pref_service_ = pref_service; |
| oom_kills_daily_count_ = |
| pref_service_->GetInteger(::prefs::kOOMKillsDailyCount); |
| |
| daily_event_ = std::make_unique<::metrics::DailyEvent>( |
| pref_service, ::prefs::kOOMKillsDailySample, |
| kOOMKillsDailyEventHistogramName); |
| daily_event_->AddObserver(std::make_unique<DailyEventObserver>(this)); |
| daily_event_->CheckInterval(); |
| daily_event_timer_.Start(FROM_HERE, kDailyEventIntervalTimeDelta, |
| daily_event_.get(), |
| &::metrics::DailyEvent::CheckInterval); |
| } |
| |
| // Both host and guest(ARCVM) oom kills are logged to the same histogram |
| // Memory.OOMKills.Count. |
| // |
| // TODO(vovoy): Log ARCVM oom kills to a new histogram. |
| void OOMKillsMonitor::LogArcOOMKill(unsigned long current_oom_kills) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(monitoring_started_); |
| |
| unsigned long oom_kills_delta = current_oom_kills - last_arc_oom_kills_count_; |
| if (oom_kills_delta == 0) |
| return; |
| |
| VLOG(1) << "ARC_OOM_KILLS " << oom_kills_delta << " times"; |
| |
| ReportOOMKills(oom_kills_delta); |
| |
| last_arc_oom_kills_count_ = current_oom_kills; |
| } |
| |
| void OOMKillsMonitor::StopTimersForTesting() { |
| checking_timer_.Stop(); |
| daily_event_timer_.Stop(); |
| } |
| |
| void OOMKillsMonitor::TriggerDailyEventForTesting() { |
| ReportDailyMetrics(::metrics::DailyEvent::IntervalType::DAY_ELAPSED); |
| } |
| |
| void OOMKillsMonitor::CheckOOMKill() { |
| base::VmStatInfo vmstat; |
| if (base::GetVmStatInfo(&vmstat)) { |
| CheckOOMKillImpl(vmstat.oom_kill); |
| } |
| } |
| |
| void OOMKillsMonitor::CheckOOMKillImpl(unsigned long current_oom_kills) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(monitoring_started_); |
| |
| unsigned long oom_kills_delta = current_oom_kills - last_oom_kills_count_; |
| if (oom_kills_delta == 0) |
| return; |
| |
| VLOG(1) << "OOM_KILLS " << oom_kills_delta << " times"; |
| |
| ReportOOMKills(oom_kills_delta); |
| |
| last_oom_kills_count_ = current_oom_kills; |
| } |
| |
| void OOMKillsMonitor::ReportOOMKills(unsigned long oom_kills_delta) { |
| for (size_t i = 0; i < oom_kills_delta; ++i) { |
| ++oom_kills_count_; |
| |
| // Report the cumulative count of killed process. For example if there are |
| // 3 processes killed, it would report 1 for the first kill, 2 for the |
| // second kill, then 3 for the final kill. |
| UmaHistogramOOMKills(oom_kills_count_); |
| } |
| |
| oom_kills_daily_count_ += oom_kills_delta; |
| pref_service_->SetInteger(::prefs::kOOMKillsDailyCount, |
| oom_kills_daily_count_); |
| } |
| |
| void OOMKillsMonitor::ReportDailyMetrics( |
| ::metrics::DailyEvent::IntervalType type) { |
| if (type == ::metrics::DailyEvent::IntervalType::DAY_ELAPSED) { |
| base::UmaHistogramCounts10000(kOOMKillsDailyHistogramName, |
| oom_kills_daily_count_); |
| } |
| |
| // There are 3 interval types: FIRST_RUN, DAY_ELAPSED, CLOCK_CHANGED. The |
| // counter should be reset in all 3 cases. |
| oom_kills_daily_count_ = 0; |
| pref_service_->SetInteger(::prefs::kOOMKillsDailyCount, 0); |
| } |
| |
| } // namespace memory |