Consolidate code monitoring low memory kills and OOM kills to MemoryKillsMonitor on ChromeOS
Rename OomKillsMonitor to MemoryKillsMonitor and move it from
arc namespace to memory namespace. Now it is responsible for
1. Log OOM kill events by listening to kernel messages (in a dedicated thread).
2. Log low memory kill events when TabManager kills processes (called from UI thread).
It logs those events to
1. Chrome UMA
2. A local file if --memory-kills-log is given
It starts a new monitoring session when a new BrowserProcess is created.
BUG=none
TEST=manual
Review-Url: https://codereview.chromium.org/2527973003
Cr-Commit-Position: refs/heads/master@{#435743}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 4779a4b..6aae3e3d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2172,6 +2172,9 @@
"download/notification/download_notification.h",
"download/notification/download_notification_manager.cc",
"download/notification/download_notification_manager.h",
+ "memory/memory_kills_histogram.h",
+ "memory/memory_kills_monitor.cc",
+ "memory/memory_kills_monitor.h",
"metrics/chromeos_metrics_provider.cc",
"metrics/chromeos_metrics_provider.h",
"metrics/leak_detector/leak_detector_controller.cc",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index fc3ef09..19f18b12 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1389,6 +1389,7 @@
"../extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc",
"../extensions/api/log_private/syslog_parser_unittest.cc",
"../extensions/updater/local_extension_cache_unittest.cc",
+ "../memory/memory_kills_monitor_unittest.cc",
"../metrics/chromeos_metrics_provider_unittest.cc",
"../metrics/leak_detector/leak_detector_controller_unittest.cc",
"../metrics/perf/cpu_identity_unittest.cc",
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 8cd1f92..b9b4a980 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -354,6 +354,10 @@
chrome::SetChannel(channel);
#endif
+ // Start monitoring OOM kills.
+ memory_kills_monitor_ = base::MakeUnique<memory::MemoryKillsMonitor::Handle>(
+ memory::MemoryKillsMonitor::StartMonitoring());
+
ChromeBrowserMainPartsLinux::PreEarlyInitialization();
}
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index fb33160..cea135d 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -11,6 +11,7 @@
#include "base/task/cancelable_task_tracker.h"
#include "chrome/browser/chrome_browser_main_linux.h"
#include "chrome/browser/chromeos/external_metrics.h"
+#include "chrome/browser/memory/memory_kills_monitor.h"
#include "chromeos/system/version_loader.h"
namespace session_manager {
@@ -95,6 +96,8 @@
std::unique_ptr<LowDiskNotification> low_disk_notification_;
std::unique_ptr<ArcKioskAppManager> arc_kiosk_app_manager_;
+ std::unique_ptr<memory::MemoryKillsMonitor::Handle> memory_kills_monitor_;
+
DISALLOW_COPY_AND_ASSIGN(ChromeBrowserMainPartsChromeos);
};
diff --git a/chrome/browser/memory/memory_kills_histogram.h b/chrome/browser/memory/memory_kills_histogram.h
new file mode 100644
index 0000000..67f1641
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_histogram.h
@@ -0,0 +1,25 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEMORY_MEMORY_KILLS_HISTOGRAM_H_
+#define CHROME_BROWSER_MEMORY_MEMORY_KILLS_HISTOGRAM_H_
+
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+
+namespace memory {
+
+constexpr base::TimeDelta kMaxMemoryKillTimeDelta =
+ base::TimeDelta::FromSeconds(30);
+
+} // namespace memory
+
+// Use this macro to report elapsed time since last Memory kill event.
+// Must be a macro as the underlying HISTOGRAM macro creates static variables.
+#define UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(name, sample) \
+ UMA_HISTOGRAM_CUSTOM_TIMES( \
+ name, sample, base::TimeDelta::FromMilliseconds(1), \
+ ::memory::kMaxMemoryKillTimeDelta, 50)
+
+#endif // CHROME_BROWSER_MEMORY_MEMORY_KILLS_HISTOGRAM_H_
diff --git a/chrome/browser/memory/memory_kills_monitor.cc b/chrome/browser/memory/memory_kills_monitor.cc
new file mode 100644
index 0000000..9e6da116
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_monitor.cc
@@ -0,0 +1,218 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/memory/memory_kills_monitor.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/leak_annotations.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/posix/safe_strerror.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/time/time.h"
+#include "chrome/browser/memory/memory_kills_histogram.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace memory {
+
+using base::SequencedWorkerPool;
+using base::TimeDelta;
+
+namespace {
+
+int64_t GetTimestamp(const std::string& line) {
+ std::vector<std::string> fields = base::SplitString(
+ line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ int64_t timestamp = -1;
+ // Timestamp is the third field in a line of /dev/kmsg.
+ if (fields.size() < 3 || !base::StringToInt64(fields[2], ×tamp))
+ return -1;
+ return timestamp;
+}
+
+void LogEvent(const base::Time& time_stamp, const std::string& event) {
+ VLOG(1) << time_stamp.ToJavaTime() << ", " << event;
+}
+
+void LogOOMKill(int64_t time_stamp, int oom_badness) {
+ static int64_t last_kill_time = -1;
+ static int oom_kills = 0;
+
+ // Ideally the timestamp should be parsed from /dev/kmsg, but the timestamp
+ // there is the elapsed time since system boot. So the timestamp |now| used
+ // here is a bit delayed.
+ base::Time now = base::Time::Now();
+ LogEvent(now, "OOM_KILL");
+
+ ++oom_kills;
+ // Report the cumulative count of killed process in one login session.
+ // 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.
+ // It doesn't report a final count at the end of a user session because
+ // the code runs in a dedicated thread and never ends until browser shutdown
+ // (or logout on Chrome OS). And on browser shutdown the thread may be
+ // terminated brutally so there's no chance to execute a "final" block.
+ // More specifically, code outside the main loop of MemoryKillsMonitor::Run()
+ // are not guaranteed to be executed.
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Arc.OOMKills.Count", oom_kills, 1, 1000, 1001);
+
+ // In practice most process has oom_badness < 1000, but
+ // strictly speaking the number could be [1, 2000]. What it really
+ // means is the baseline, proportion of memory used (normalized to
+ // [0, 1000]), plus an adjustment score oom_score_adj [-1000, 1000],
+ // truncated to 1 if negative (0 means never kill).
+ // Ref: https://lwn.net/Articles/396552/
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Arc.OOMKills.Score", oom_badness, 1, 2000, 2001);
+
+ if (time_stamp > 0) {
+ // Sets to |kMaxMemoryKillTimeDelta| for the first kill event.
+ const TimeDelta time_delta =
+ last_kill_time < 0 ? kMaxMemoryKillTimeDelta:
+ TimeDelta::FromMicroseconds(time_stamp - last_kill_time);
+
+ last_kill_time = time_stamp;
+
+ UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(
+ "Arc.OOMKills.TimeDelta", time_delta);
+ }
+}
+
+} // namespace
+
+MemoryKillsMonitor::Handle::Handle(MemoryKillsMonitor* outer) : outer_(outer) {
+ DCHECK(outer_);
+}
+
+MemoryKillsMonitor::Handle::Handle(MemoryKillsMonitor::Handle&& other)
+ : outer_(nullptr) {
+ outer_ = other.outer_;
+ other.outer_ = nullptr;
+}
+
+MemoryKillsMonitor::Handle::~Handle() {
+ if (outer_) {
+ VLOG(2) << "Chrome is shutting down" << outer_;
+ outer_->is_shutting_down_.Set();
+ }
+}
+
+MemoryKillsMonitor::MemoryKillsMonitor() {
+ base::SimpleThread::Options non_joinable_options;
+ non_joinable_options.joinable = false;
+ non_joinable_worker_thread_ = base::MakeUnique<base::DelegateSimpleThread>(
+ this, "memory_kills_monitor", non_joinable_options);
+ non_joinable_worker_thread_->Start();
+}
+
+MemoryKillsMonitor::~MemoryKillsMonitor() {
+ // The instance has to be leaked on shutdown as it is referred to by a
+ // non-joinable thread but ~MemoryKillsMonitor() can't be explicitly deleted
+ // as it overrides ~SimpleThread(), it should nevertheless never be invoked.
+ NOTREACHED();
+}
+
+// static
+MemoryKillsMonitor::Handle MemoryKillsMonitor::StartMonitoring() {
+#if DCHECK_IS_ON()
+ static volatile bool monitoring_active = false;
+ DCHECK(!monitoring_active);
+ monitoring_active = true;
+#endif
+
+ // Instantiate the MemoryKillsMonitor and its underlying thread. The
+ // MemoryKillsMonitor itself has to be leaked on shutdown per having a
+ // non-joinable thread associated to its state. The MemoryKillsMonitor::Handle
+ // will notify the MemoryKillsMonitor when it is destroyed so that the
+ // underlying thread can at a minimum not do extra work during shutdown.
+ MemoryKillsMonitor* instance = new MemoryKillsMonitor();
+ ANNOTATE_LEAKING_OBJECT_PTR(instance);
+ return Handle(instance);
+}
+
+// static
+void MemoryKillsMonitor::LogLowMemoryKill(
+ const std::string& type, int estimated_freed_kb) {
+ static base::Time last_kill_time;
+ static int low_memory_kills = 0;
+
+ base::Time now = base::Time::Now();
+ LogEvent(now, "LOW_MEMORY_KILL_" + type);
+
+ const TimeDelta time_delta =
+ last_kill_time.is_null() ?
+ kMaxMemoryKillTimeDelta :
+ (now - last_kill_time);
+ UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(
+ "Arc.LowMemoryKiller.TimeDelta", time_delta);
+ last_kill_time = now;
+
+ ++low_memory_kills;
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Arc.LowMemoryKiller.Count", low_memory_kills, 1, 1000, 1001);
+
+ UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize",
+ estimated_freed_kb);
+}
+
+// static
+void MemoryKillsMonitor::TryMatchOomKillLine(const std::string& line) {
+ // Sample OOM log line:
+ // 3,1362,97646497541,-;Out of memory: Kill process 29582 (android.vending)
+ // score 961 or sacrifice child.
+ int oom_badness;
+ TimeDelta time_delta;
+ if (RE2::PartialMatch(line,
+ "Out of memory: Kill process .* score (\\d+)",
+ &oom_badness)) {
+ int64_t time_stamp = GetTimestamp(line);
+ LogOOMKill(time_stamp, oom_badness);
+ }
+}
+
+void MemoryKillsMonitor::Run() {
+ VLOG(1) << "MemoryKillsMonitor started";
+ base::ScopedFILE kmsg_handle(
+ base::OpenFile(base::FilePath("/dev/kmsg"), "r"));
+ if (!kmsg_handle) {
+ LOG(WARNING) << "Open /dev/kmsg failed: " << base::safe_strerror(errno);
+ return;
+ }
+ // Skip kernel messages prior to the instantiation of this object to avoid
+ // double reporting.
+ fseek(kmsg_handle.get(), 0, SEEK_END);
+
+ static constexpr int kMaxBufSize = 512;
+ char buf[kMaxBufSize];
+
+ while (fgets(buf, kMaxBufSize, kmsg_handle.get())) {
+ if (is_shutting_down_.IsSet()) {
+ // Not guaranteed to execute when the process is shutting down,
+ // because the thread might be blocked in fgets().
+ VLOG(1) << "Chrome is shutting down, MemoryKillsMonitor exits.";
+ break;
+ }
+ TryMatchOomKillLine(buf);
+ }
+}
+
+
+} // namespace memory
diff --git a/chrome/browser/memory/memory_kills_monitor.h b/chrome/browser/memory/memory_kills_monitor.h
new file mode 100644
index 0000000..8594f53
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_monitor.h
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MEMORY_MEMORY_KILLS_MONITOR_H_
+#define CHROME_BROWSER_MEMORY_MEMORY_KILLS_MONITOR_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+
+namespace memory {
+
+// Traces kernel OOM kill events and Low memory kill events (by Chrome
+// TabManager).
+//
+// For OOM kill events, it listens to kernel message (/dev/kmsg) in a blocking
+// manner. It runs in a non-joinable thread in order to avoid blocking shutdown.
+// There should be only one MemoryKillsMonitor instance globally at any given
+// time, otherwise UMA would receive duplicate events.
+//
+// For Low memory kills events, chrome calls the single global instance of
+// MemoryKillsMonitor synchronously. Note that it would be from a browser thread
+// other than the listening thread.
+//
+// For every events, it reports to UMA and optionally a local file specified by
+// --memory-kills-log. The log file is useful if we want to analyze low memory
+// kills. If the flag is not given, it won't write to any file.
+class MemoryKillsMonitor : public base::DelegateSimpleThread::Delegate {
+ public:
+ // A handle representing the MemoryKillsMonitor's lifetime (the monitor itself
+ // can't be destroyed per being a non-joinable Thread).
+ class Handle {
+ public:
+ // Constructs a handle that will flag |outer| as shutting down on
+ // destruction.
+ explicit Handle(MemoryKillsMonitor* outer);
+
+ Handle(Handle&& handle);
+
+ ~Handle();
+
+ private:
+ MemoryKillsMonitor* outer_;
+ DISALLOW_COPY_AND_ASSIGN(Handle);
+ };
+
+ // Instantiates the MemoryKillsMonitor instance and starts it. This must only
+ // be invoked once per process.
+ static Handle StartMonitoring();
+
+ // Logs a low memory kill event.
+ static void LogLowMemoryKill(const std::string& type, int estimated_freed_kb);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(MemoryKillsMonitorTest, TryMatchOomKillLine);
+
+ MemoryKillsMonitor();
+ ~MemoryKillsMonitor() override;
+
+ // Overridden from base::DelegateSimpleThread::Delegate:
+ void Run() override;
+
+ // Try to match a line in kernel message which reports OOM.
+ static void TryMatchOomKillLine(const std::string& line);
+
+ // A flag set when MemoryKillsMonitor is shutdown so that its thread can poll
+ // it and attempt to wind down from that point (to avoid unnecessary work, not
+ // because it blocks shutdown).
+ base::AtomicFlag is_shutting_down_;
+
+ // The underlying worker thread which is non-joinable to avoid blocking
+ // shutdown.
+ std::unique_ptr<base::DelegateSimpleThread> non_joinable_worker_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryKillsMonitor);
+};
+
+} // namespace memory
+
+#endif // CHROME_BROWSER_MEMORY_MEMORY_KILLS_MONITOR_H_
diff --git a/chrome/browser/memory/memory_kills_monitor_unittest.cc b/chrome/browser/memory/memory_kills_monitor_unittest.cc
new file mode 100644
index 0000000..339030e
--- /dev/null
+++ b/chrome/browser/memory/memory_kills_monitor_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/memory/memory_kills_monitor.h"
+
+#include "base/macros.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/time/time.h"
+#include "chrome/browser/memory/memory_kills_histogram.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace memory {
+
+using MemoryKillsMonitorTest = testing::Test;
+
+TEST_F(MemoryKillsMonitorTest, LogLowMemoryKill) {
+ MemoryKillsMonitor::LogLowMemoryKill("APP", 123);
+ MemoryKillsMonitor::LogLowMemoryKill("APP", 100);
+ MemoryKillsMonitor::LogLowMemoryKill("TAB", 10000);
+
+ auto histogram_count =
+ base::StatisticsRecorder::FindHistogram("Arc.LowMemoryKiller.Count");
+ ASSERT_TRUE(histogram_count);
+ auto count_samples = histogram_count->SnapshotSamples();
+ EXPECT_EQ(3, count_samples->TotalCount());
+ EXPECT_EQ(1, count_samples->GetCount(1));
+ EXPECT_EQ(1, count_samples->GetCount(2));
+ EXPECT_EQ(1, count_samples->GetCount(3));
+
+ auto histogram_freed_size =
+ base::StatisticsRecorder::FindHistogram("Arc.LowMemoryKiller.FreedSize");
+ ASSERT_TRUE(histogram_freed_size);
+ auto freed_size_samples = histogram_freed_size->SnapshotSamples();
+ EXPECT_EQ(3, freed_size_samples->TotalCount());
+ // 123 and 100 are in the same bucket.
+ EXPECT_EQ(2, freed_size_samples->GetCount(123));
+ EXPECT_EQ(2, freed_size_samples->GetCount(100));
+ EXPECT_EQ(1, freed_size_samples->GetCount(10000));
+
+ auto histogram_time_delta =
+ base::StatisticsRecorder::FindHistogram("Arc.LowMemoryKiller.TimeDelta");
+ ASSERT_TRUE(histogram_time_delta);
+ auto time_delta_samples = histogram_time_delta->SnapshotSamples();
+ EXPECT_EQ(3, time_delta_samples->TotalCount());
+ // First time delta is set to kMaxMemoryKillTimeDelta.
+ EXPECT_EQ(1, time_delta_samples->GetCount(
+ kMaxMemoryKillTimeDelta.InMilliseconds()));
+ // Time delta for the other 2 events depends on Now() so we skip testing it
+ // here.
+}
+
+TEST_F(MemoryKillsMonitorTest, TryMatchOomKillLine) {
+ const char* sample_lines[] = {
+ "3,3429,812967386,-;Out of memory: Kill process 8291 (handle-watcher-) "
+ "score 674 or sacrifice child",
+ "3,3431,812981331,-;Out of memory: Kill process 8271 (.gms.persistent) "
+ "score 652 or sacrifice child",
+ "3,3433,812993014,-;Out of memory: Kill process 9210 (lowpool[11]) "
+ "score 653 or sacrifice child"
+ };
+
+ for (unsigned long i = 0; i < arraysize(sample_lines); ++i) {
+ MemoryKillsMonitor::TryMatchOomKillLine(sample_lines[i]);
+ }
+
+ auto histogram_count =
+ base::StatisticsRecorder::FindHistogram("Arc.OOMKills.Count");
+ ASSERT_TRUE(histogram_count);
+ auto count_samples = histogram_count->SnapshotSamples();
+ EXPECT_EQ(3, count_samples->TotalCount());
+ EXPECT_EQ(1, count_samples->GetCount(1));
+ EXPECT_EQ(1, count_samples->GetCount(2));
+ EXPECT_EQ(1, count_samples->GetCount(3));
+
+ auto histogram_score =
+ base::StatisticsRecorder::FindHistogram("Arc.OOMKills.Score");
+ ASSERT_TRUE(histogram_score);
+ auto score_samples = histogram_score->SnapshotSamples();
+ EXPECT_EQ(3, score_samples->TotalCount());
+ EXPECT_EQ(1, score_samples->GetCount(674));
+ EXPECT_EQ(1, score_samples->GetCount(652));
+ EXPECT_EQ(1, score_samples->GetCount(653));
+
+ auto histogram_time_delta =
+ base::StatisticsRecorder::FindHistogram("Arc.OOMKills.TimeDelta");
+ ASSERT_TRUE(histogram_time_delta);
+ auto time_delta_samples = histogram_time_delta->SnapshotSamples();
+ EXPECT_EQ(3, time_delta_samples->TotalCount());
+ // First time delta is set to kMaxMemoryKillTimeDelta.
+ EXPECT_EQ(1, time_delta_samples->GetCount(
+ kMaxMemoryKillTimeDelta.InMilliseconds()));
+ EXPECT_EQ(1, time_delta_samples->GetCount(11));
+ EXPECT_EQ(1, time_delta_samples->GetCount(13));
+}
+
+} // namespace memory
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.cc b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
index 75100d0..b9f1d3e 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.cc
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.cc
@@ -27,6 +27,7 @@
#include "base/time/time.h"
#include "chrome/browser/chromeos/arc/process/arc_process.h"
#include "chrome/browser/chromeos/arc/process/arc_process_service.h"
+#include "chrome/browser/memory/memory_kills_monitor.h"
#include "chrome/browser/memory/tab_stats.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
@@ -36,7 +37,6 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/common/process.mojom.h"
-#include "components/arc/metrics/oom_kills_histogram.h"
#include "components/exo/shell_surface.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
@@ -273,36 +273,6 @@
return mem_usage.priv;
}
-class TabManagerDelegate::UmaReporter {
- public:
- UmaReporter() : total_kills_(0) {}
- ~UmaReporter() {}
-
- void ReportKill(const int memory_freed);
-
- private:
- base::Time last_kill_time_;
- int total_kills_;
-};
-
-void TabManagerDelegate::UmaReporter::ReportKill(const int memory_freed) {
- base::Time now = base::Time::Now();
- const TimeDelta time_delta =
- last_kill_time_.is_null() ?
- TimeDelta::FromSeconds(arc::kMaxOomMemoryKillTimeDeltaSecs) :
- (now - last_kill_time_);
- UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
- "Arc.LowMemoryKiller.TimeDelta", time_delta);
- last_kill_time_ = now;
-
- ++total_kills_;
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.LowMemoryKiller.Count", total_kills_, 1, 1000, 1001);
-
- UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize",
- memory_freed);
-}
-
TabManagerDelegate::TabManagerDelegate(
const base::WeakPtr<TabManager>& tab_manager)
: TabManagerDelegate(tab_manager, new MemoryStat()) {
@@ -314,7 +284,6 @@
: tab_manager_(tab_manager),
focused_process_(new FocusedProcess()),
mem_stat_(mem_stat),
- uma_(new UmaReporter()),
weak_ptr_factory_(this) {
registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
content::NotificationService::AllBrowserContextsAndSources());
@@ -631,7 +600,7 @@
mem_stat_->EstimatedMemoryFreedKB(it->app()->pid());
if (KillArcProcess(it->app()->nspid())) {
target_memory_to_free_kb -= estimated_memory_freed_kb;
- uma_->ReportKill(estimated_memory_freed_kb);
+ MemoryKillsMonitor::LogLowMemoryKill("APP", estimated_memory_freed_kb);
VLOG(2) << "Killed " << *it;
}
} else {
@@ -643,7 +612,7 @@
mem_stat_->EstimatedMemoryFreedKB(it->tab()->renderer_handle);
if (KillTab(tab_id)) {
target_memory_to_free_kb -= estimated_memory_freed_kb;
- uma_->ReportKill(estimated_memory_freed_kb);
+ MemoryKillsMonitor::LogLowMemoryKill("TAB", estimated_memory_freed_kb);
VLOG(2) << "Killed " << *it;
}
}
diff --git a/chrome/browser/memory/tab_manager_delegate_chromeos.h b/chrome/browser/memory/tab_manager_delegate_chromeos.h
index 13c6c8b..af53e01 100644
--- a/chrome/browser/memory/tab_manager_delegate_chromeos.h
+++ b/chrome/browser/memory/tab_manager_delegate_chromeos.h
@@ -97,7 +97,6 @@
class Candidate;
class FocusedProcess;
- class UmaReporter;
friend std::ostream& operator<<(std::ostream& out,
const Candidate& candidate);
@@ -169,9 +168,6 @@
// Util for getting system memory status.
std::unique_ptr<TabManagerDelegate::MemoryStat> mem_stat_;
- // Reports UMA histograms.
- std::unique_ptr<UmaReporter> uma_;
-
// Weak pointer factory used for posting tasks to other threads.
base::WeakPtrFactory<TabManagerDelegate> weak_ptr_factory_;
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 99aef6c0..c09d970 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -56,9 +56,6 @@
"kiosk/arc_kiosk_bridge.h",
"metrics/arc_metrics_service.cc",
"metrics/arc_metrics_service.h",
- "metrics/oom_kills_histogram.h",
- "metrics/oom_kills_monitor.cc",
- "metrics/oom_kills_monitor.h",
"net/arc_net_host_impl.cc",
"net/arc_net_host_impl.h",
"obb_mounter/arc_obb_mounter_bridge.cc",
diff --git a/components/arc/metrics/arc_metrics_service.cc b/components/arc/metrics/arc_metrics_service.cc
index 9914eae..a7f663a 100644
--- a/components/arc/metrics/arc_metrics_service.cc
+++ b/components/arc/metrics/arc_metrics_service.cc
@@ -29,7 +29,6 @@
: ArcService(bridge_service),
binding_(this),
process_observer_(this),
- oom_kills_monitor_handle_(OomKillsMonitor::StartMonitoring()),
weak_ptr_factory_(this) {
arc_bridge_service()->metrics()->AddObserver(this);
arc_bridge_service()->process()->AddObserver(&process_observer_);
diff --git a/components/arc/metrics/arc_metrics_service.h b/components/arc/metrics/arc_metrics_service.h
index c3d256b4..7ad1a0be3 100644
--- a/components/arc/metrics/arc_metrics_service.h
+++ b/components/arc/metrics/arc_metrics_service.h
@@ -15,7 +15,6 @@
#include "components/arc/common/metrics.mojom.h"
#include "components/arc/common/process.mojom.h"
#include "components/arc/instance_holder.h"
-#include "components/arc/metrics/oom_kills_monitor.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace arc {
@@ -73,8 +72,6 @@
base::ThreadChecker thread_checker_;
base::RepeatingTimer timer_;
- OomKillsMonitor::Handle oom_kills_monitor_handle_;
-
base::TimeTicks arc_start_time_;
// Always keep this the last member of this class to make sure it's the
diff --git a/components/arc/metrics/oom_kills_histogram.h b/components/arc/metrics/oom_kills_histogram.h
deleted file mode 100644
index 2a9bf56..0000000
--- a/components/arc/metrics/oom_kills_histogram.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
-#define COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
-
-#include "base/metrics/histogram.h"
-
-namespace arc {
-
-const int kMaxOomMemoryKillTimeDeltaSecs = 30;
-
-} // namespace arc
-
-// Use this macro to report elapsed time since last OOM kill event.
-// Must be a macro as the underlying HISTOGRAM macro creates static variables.
-#define UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(name, sample) \
- UMA_HISTOGRAM_CUSTOM_TIMES( \
- name, sample, base::TimeDelta::FromMilliseconds(1), \
- base::TimeDelta::FromSeconds(::arc::kMaxOomMemoryKillTimeDeltaSecs), 50)
-
-#endif // COMPONENTS_ARC_METRICS_OOM_KILLS_HISTOGRAM_H_
diff --git a/components/arc/metrics/oom_kills_monitor.cc b/components/arc/metrics/oom_kills_monitor.cc
deleted file mode 100644
index 719b778..0000000
--- a/components/arc/metrics/oom_kills_monitor.cc
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/arc/metrics/oom_kills_monitor.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/debug/leak_annotations.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/posix/safe_strerror.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/string_split.h"
-#include "base/time/time.h"
-#include "components/arc/metrics/oom_kills_histogram.h"
-#include "third_party/re2/src/re2/re2.h"
-#include "third_party/re2/src/re2/stringpiece.h"
-
-namespace arc {
-
-using base::StringPiece;
-
-using base::SequencedWorkerPool;
-using base::TimeDelta;
-
-namespace {
-
-int64_t GetTimestamp(const StringPiece& line) {
- std::vector<StringPiece> fields = base::SplitStringPiece(
- line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
- int64_t timestamp = -1;
- // Timestamp is the third field in a line of /dev/kmsg.
- if (fields.size() < 3 || !base::StringToInt64(fields[2], ×tamp))
- return -1;
- return timestamp;
-}
-
-bool GetTimeDelta(
- const StringPiece& line, int64_t* last, TimeDelta* time_delta) {
- int64_t now = GetTimestamp(line);
- if (now < 0)
- return false;
-
- // Sets to |kMaxOomMemoryKillTimeSecs| for the first kill event.
- if (*last < 0)
- *time_delta = TimeDelta::FromSeconds(kMaxOomMemoryKillTimeDeltaSecs);
- else
- *time_delta = TimeDelta::FromMicroseconds(now - *last);
-
- *last = now;
-
- return true;
-}
-
-void LogOOMKill(const StringPiece& line) {
- static int64_t last_timestamp = -1;
- static int oom_kills = 0;
-
- // Sample log line:
- // 3,1362,97646497541,-;Out of memory: Kill process 29582 (android.vending)
- // score 961 or sacrifice child.
- int oom_badness;
- TimeDelta time_delta;
- if (RE2::PartialMatch(re2::StringPiece(line.data(), line.size()),
- "Out of memory: Kill process .* score (\\d+)",
- &oom_badness)) {
- ++oom_kills;
- // Report the cumulative count of killed process in one login session.
- // 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.
- // It doesn't report a final count at the end of a user session because
- // the code runs in a dedicated thread and never ends until browser shutdown
- // (or logout on Chrome OS). And on browser shutdown the thread may be
- // terminated brutally so there's no chance to execute a "final" block.
- // More specifically, code outside the main loop of OomKillsMonitor::Run()
- // are not guaranteed to be executed.
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.OOMKills.Count", oom_kills, 1, 1000, 1001);
-
- // In practice most process has oom_badness < 1000, but
- // strictly speaking the number could be [1, 2000]. What it really
- // means is the baseline, proportion of memory used (normalized to
- // [0, 1000]), plus an adjustment score oom_score_adj [-1000, 1000],
- // truncated to 1 if negative (0 means never kill).
- // Ref: https://lwn.net/Articles/396552/
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.OOMKills.Score", oom_badness, 1, 2000, 2001);
-
- if (GetTimeDelta(line, &last_timestamp, &time_delta)) {
- UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
- "Arc.OOMKills.TimeDelta", time_delta);
- }
- }
-}
-
-void LogLowMemoryKill(const StringPiece& line) {
- static int64_t last_timestamp = -1;
- static int low_memory_kills = 0;
-
- int freed_size;
- TimeDelta time_delta;
- // Sample log line:
- // 6,2302,533604004,-;lowmemorykiller: Killing 'externalstorage' (21742),
- // adj 1000,\x0a to free 27320kB on behalf of 'kswapd0' (47) because\x0a
- // cache 181920kB is below limit 184320kB for oom_score_adj 1000\x0a
- // Free memory is 1228kB above reserved
- if (RE2::PartialMatch(re2::StringPiece(line.data(), line.size()),
- "lowmemorykiller: .* to free (\\d+)kB",
- &freed_size)) {
- // Report the count for each lowmemorykill event. See comments in
- // LogOOMKill().
- ++low_memory_kills;
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Arc.LowMemoryKiller.Count", low_memory_kills, 1, 1000, 1001);
-
- UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize", freed_size);
-
- if (GetTimeDelta(line, &last_timestamp, &time_delta)) {
- UMA_HISTOGRAM_OOM_KILL_TIME_INTERVAL(
- "Arc.LowMemoryKiller.TimeDelta", time_delta);
- }
- }
-}
-
-} // namespace
-
-OomKillsMonitor::Handle::Handle(OomKillsMonitor* outer) : outer_(outer) {
- DCHECK(outer_);
-}
-
-OomKillsMonitor::Handle::~Handle() {
- outer_->is_shutting_down_.Set();
-}
-
-OomKillsMonitor::OomKillsMonitor() {
- base::SimpleThread::Options non_joinable_options;
- non_joinable_options.joinable = false;
- non_joinable_worker_thread_ = base::MakeUnique<base::DelegateSimpleThread>(
- this, "oom_kills_monitor", non_joinable_options);
- non_joinable_worker_thread_->Start();
-}
-
-OomKillsMonitor::~OomKillsMonitor() {
- // The instance has to be leaked on shutdown as it is referred to by a
- // non-joinable thread but ~OomKillsMonitor() can't be explicitly deleted as
- // it overrides ~SimpleThread(), it should nevertheless never be invoked.
- NOTREACHED();
-}
-
-// static
-OomKillsMonitor::Handle OomKillsMonitor::StartMonitoring() {
-#if DCHECK_IS_ON()
- static volatile bool monitoring_active = false;
- DCHECK(!monitoring_active);
- monitoring_active = true;
-#endif
-
- // Instantiate the OomKillsMonitor and its underlying thread. The
- // OomKillsMonitor itself has to be leaked on shutdown per having a
- // non-joinable thread associated to its state. The OomKillsMonitor::Handle
- // will notify the OomKillsMonitor when it is destroyed so that the underlying
- // thread can at a minimum not do extra work during shutdown.
- OomKillsMonitor* instance = new OomKillsMonitor;
- ANNOTATE_LEAKING_OBJECT_PTR(instance);
- return Handle(instance);
-}
-
-void OomKillsMonitor::Run() {
- base::ScopedFILE kmsg_handle(
- base::OpenFile(base::FilePath("/dev/kmsg"), "r"));
- if (!kmsg_handle) {
- LOG(WARNING) << "Open /dev/kmsg failed: " << base::safe_strerror(errno);
- return;
- }
- // Skip kernel messages prior to the instantiation of this object to avoid
- // double reporting.
- fseek(kmsg_handle.get(), 0, SEEK_END);
-
- static const int kMaxBufSize = 512;
- char buf[kMaxBufSize];
-
- while (fgets(buf, kMaxBufSize, kmsg_handle.get())) {
- if (is_shutting_down_.IsSet()) {
- DVLOG(1) << "Chrome is shutting down, exit now.";
- break;
- }
- const StringPiece buf_string(buf);
- LogOOMKill(buf_string);
- LogLowMemoryKill(buf_string);
- }
-}
-
-} // namespace arc
diff --git a/components/arc/metrics/oom_kills_monitor.h b/components/arc/metrics/oom_kills_monitor.h
deleted file mode 100644
index 63e249e..0000000
--- a/components/arc/metrics/oom_kills_monitor.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
-#define COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/atomic_flag.h"
-#include "base/threading/simple_thread.h"
-
-namespace arc {
-
-// Traces kernel OOM kill events and lowmemorykiller (if enabled) events.
-//
-// OomKillsMonitor listens to kernel messages for both OOM kills and
-// lowmemorykiller kills, then reports to UMA. It uses a non-joinable thread
-// in order to avoid blocking shutdown.
-//
-// Note: There should be only one OomKillsMonitor instance globally at any given
-// time, otherwise UMA would receive duplicate events.
-class OomKillsMonitor : public base::DelegateSimpleThread::Delegate {
- public:
- // A handle representing the OomKillsMonitor's lifetime (the monitor itself
- // can't be destroyed per being a non-joinable Thread).
- class Handle {
- public:
- // Constructs a handle that will flag |outer| as shutting down on
- // destruction.
- explicit Handle(OomKillsMonitor* outer);
-
- ~Handle();
-
- private:
- OomKillsMonitor* const outer_;
- };
-
- // Instantiates the OomKillsMonitor instance and starts it. This must only
- // be invoked once per process.
- static Handle StartMonitoring();
-
- private:
- OomKillsMonitor();
- ~OomKillsMonitor() override;
-
- // Overridden from base::DelegateSimpleThread::Delegate:
- void Run() override;
-
- // A flag set when OomKillsMonitor is shutdown so that its thread can poll
- // it and attempt to wind down from that point (to avoid unnecessary work, not
- // because it blocks shutdown).
- base::AtomicFlag is_shutting_down_;
-
- // The underlying worker thread which is non-joinable to avoid blocking
- // shutdown.
- std::unique_ptr<base::DelegateSimpleThread> non_joinable_worker_thread_;
-
- DISALLOW_COPY_AND_ASSIGN(OomKillsMonitor);
-};
-
-} // namespace arc
-
-#endif // COMPONENTS_ARC_METRICS_OOM_KILLS_MONITOR_H_