Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: chrome/browser/memory/memory_kills_monitor.cc

Issue 2527973003: Consolidate code monitoring low memory kills and OOM kills to MemoryKillsMonitor on ChromeOS. (Closed)
Patch Set: review comments 2. fix move constructor Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/memory/memory_kills_monitor.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <inttypes.h>
10 #include <stdio.h>
11
12 #include <string>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/command_line.h"
17 #include "base/debug/leak_annotations.h"
18 #include "base/files/file_util.h"
19 #include "base/files/scoped_file.h"
20 #include "base/lazy_instance.h"
21 #include "base/location.h"
22 #include "base/logging.h"
23 #include "base/memory/ptr_util.h"
24 #include "base/metrics/histogram_macros.h"
25 #include "base/posix/safe_strerror.h"
26 #include "base/sequenced_task_runner.h"
27 #include "base/strings/string_number_conversions.h"
28 #include "base/strings/string_split.h"
29 #include "base/synchronization/atomic_flag.h"
30 #include "base/time/time.h"
31 #include "chrome/browser/memory/memory_kills_histogram.h"
32 #include "third_party/re2/src/re2/re2.h"
33
34 namespace memory {
35
36 using base::SequencedWorkerPool;
37 using base::TimeDelta;
38
39 namespace {
40
41 int64_t GetTimestamp(const std::string& line) {
42 std::vector<std::string> fields = base::SplitString(
43 line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
44
45 int64_t timestamp = -1;
46 // Timestamp is the third field in a line of /dev/kmsg.
47 if (fields.size() < 3 || !base::StringToInt64(fields[2], &timestamp))
48 return -1;
49 return timestamp;
50 }
51
52 void LogEvent(const base::Time& time_stamp, const std::string& event) {
53 VLOG(1) << time_stamp.ToJavaTime() << ", " << event;
54 }
55
56 void LogOOMKill(int64_t time_stamp, int oom_badness) {
57 static int64_t last_kill_time = -1;
58 static int oom_kills = 0;
59
60 // Ideally the timestamp should be parsed from /dev/kmsg, but the timestamp
61 // there is the elapsed time since system boot. So the timestamp |now| used
62 // here is a bit delayed.
63 base::Time now = base::Time::Now();
64 LogEvent(now, "OOM_KILL");
65
66 ++oom_kills;
67 // Report the cumulative count of killed process in one login session.
68 // For example if there are 3 processes killed, it would report 1 for the
69 // first kill, 2 for the second kill, then 3 for the final kill.
70 // It doesn't report a final count at the end of a user session because
71 // the code runs in a dedicated thread and never ends until browser shutdown
72 // (or logout on Chrome OS). And on browser shutdown the thread may be
73 // terminated brutally so there's no chance to execute a "final" block.
74 // More specifically, code outside the main loop of MemoryKillsMonitor::Run()
75 // are not guaranteed to be executed.
76 UMA_HISTOGRAM_CUSTOM_COUNTS("Arc.OOMKills.Count", oom_kills, 1, 1000, 1001);
77
78 // In practice most process has oom_badness < 1000, but
79 // strictly speaking the number could be [1, 2000]. What it really
80 // means is the baseline, proportion of memory used (normalized to
81 // [0, 1000]), plus an adjustment score oom_score_adj [-1000, 1000],
82 // truncated to 1 if negative (0 means never kill).
83 // Ref: https://lwn.net/Articles/396552/
84 UMA_HISTOGRAM_CUSTOM_COUNTS("Arc.OOMKills.Score", oom_badness, 1, 2000, 2001);
85
86 if (time_stamp > 0) {
87 // Sets to |kMaxMemoryKillTimeDelta| for the first kill event.
88 const TimeDelta time_delta =
89 last_kill_time < 0 ? kMaxMemoryKillTimeDelta:
90 TimeDelta::FromMicroseconds(time_stamp - last_kill_time);
91
92 last_kill_time = time_stamp;
93
94 UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(
95 "Arc.OOMKills.TimeDelta", time_delta);
96 }
97 }
98
99 void TryMatchOomKillLine(const std::string& line) {
100 // Sample OOM log line:
101 // 3,1362,97646497541,-;Out of memory: Kill process 29582 (android.vending)
102 // score 961 or sacrifice child.
103 int oom_badness;
104 TimeDelta time_delta;
105 if (RE2::PartialMatch(line,
106 "Out of memory: Kill process .* score (\\d+)",
107 &oom_badness)) {
108 int64_t time_stamp = GetTimestamp(line);
109 LogOOMKill(time_stamp, oom_badness);
110 }
111 }
112
113 } // namespace
114
115 MemoryKillsMonitor::Handle::Handle(MemoryKillsMonitor* outer) : outer_(outer) {
116 DCHECK(outer_);
117 }
118
119 MemoryKillsMonitor::Handle::Handle(MemoryKillsMonitor::Handle&& other)
120 : outer_(nullptr) {
121 outer_ = other.outer_;
122 other.outer_ = nullptr;
123 }
124
125 MemoryKillsMonitor::Handle::~Handle() {
126 if (outer_) {
127 VLOG(2) << "Chrome is shutting down" << outer_;
128 outer_->is_shutting_down_.Set();
129 }
130 }
131
132 MemoryKillsMonitor::MemoryKillsMonitor() {
133 base::SimpleThread::Options non_joinable_options;
134 non_joinable_options.joinable = false;
135 non_joinable_worker_thread_ = base::MakeUnique<base::DelegateSimpleThread>(
136 this, "memory_kills_monitor", non_joinable_options);
137 non_joinable_worker_thread_->Start();
138 }
139
140 MemoryKillsMonitor::~MemoryKillsMonitor() {
141 // The instance has to be leaked on shutdown as it is referred to by a
142 // non-joinable thread but ~MemoryKillsMonitor() can't be explicitly deleted
143 // as it overrides ~SimpleThread(), it should nevertheless never be invoked.
144 NOTREACHED();
145 }
146
147 // static
148 MemoryKillsMonitor::Handle MemoryKillsMonitor::StartMonitoring() {
149 #if DCHECK_IS_ON()
150 static volatile bool monitoring_active = false;
151 DCHECK(!monitoring_active);
152 monitoring_active = true;
153 #endif
154
155 // Instantiate the MemoryKillsMonitor and its underlying thread. The
156 // MemoryKillsMonitor itself has to be leaked on shutdown per having a
157 // non-joinable thread associated to its state. The MemoryKillsMonitor::Handle
158 // will notify the MemoryKillsMonitor when it is destroyed so that the
159 // underlying thread can at a minimum not do extra work during shutdown.
160 MemoryKillsMonitor* instance = new MemoryKillsMonitor();
161 ANNOTATE_LEAKING_OBJECT_PTR(instance);
162 return Handle(instance);
163 }
164
165 // static
166 void MemoryKillsMonitor::LogLowMemoryKill(
167 const std::string& type, int estimated_freed_kb) {
168 static base::Time last_kill_time;
169 static int low_memory_kills = 0;
170
171 base::Time now = base::Time::Now();
172 LogEvent(now, "LOW_MEMORY_KILL_" + type);
173
174 const TimeDelta time_delta =
175 last_kill_time.is_null() ?
176 kMaxMemoryKillTimeDelta :
177 (now - last_kill_time);
178 UMA_HISTOGRAM_MEMORY_KILL_TIME_INTERVAL(
179 "Arc.LowMemoryKiller.TimeDelta", time_delta);
180 last_kill_time = now;
181
182 ++low_memory_kills;
183 UMA_HISTOGRAM_CUSTOM_COUNTS(
184 "Arc.LowMemoryKiller.Count", low_memory_kills, 1, 1000, 1001);
185
186 UMA_HISTOGRAM_MEMORY_KB("Arc.LowMemoryKiller.FreedSize",
187 estimated_freed_kb);
188 }
189
190 void MemoryKillsMonitor::Run() {
191 VLOG(1) << "MemoryKillsMonitor started";
192 base::ScopedFILE kmsg_handle(
193 base::OpenFile(base::FilePath("/dev/kmsg"), "r"));
194 if (!kmsg_handle) {
195 LOG(WARNING) << "Open /dev/kmsg failed: " << base::safe_strerror(errno);
196 return;
197 }
198 // Skip kernel messages prior to the instantiation of this object to avoid
199 // double reporting.
200 fseek(kmsg_handle.get(), 0, SEEK_END);
201
202 static constexpr int kMaxBufSize = 512;
203 char buf[kMaxBufSize];
204
205 while (fgets(buf, kMaxBufSize, kmsg_handle.get())) {
206 if (is_shutting_down_.IsSet()) {
207 // Not guaranteed to execute when the process is shutting down,
208 // because the thread might be blocked in fgets().
209 VLOG(1) << "Chrome is shutting down, MemoryKillsMonitor exits.";
210 break;
211 }
212 TryMatchOomKillLine(buf);
213 }
214 }
215
216
217 } // namespace memory
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698