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

Side by Side Diff: chrome/browser/chromeos/power/cpu_data_collector.cc

Issue 149973002: [chromeos/about:power] Collect cpuidle and cpufreq stats (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments Created 6 years, 9 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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 <vector>
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "chrome/browser/chromeos/power/cpu_data_collector.h"
15 #include "chrome/browser/chromeos/power/power_data_collector.h"
16 #include "content/public/browser/browser_thread.h"
17
18 namespace chromeos {
19
20 namespace {
21 // The CPU data is sampled every |kCpuDataSamplePeriodSec| seconds.
22 const int kCpuDataSamplePeriodSec = 30;
23
24 // The value in the file /sys/devices/system/cpu/cpu<n>/online which indicates
25 // that CPU-n is online.
26 const int kCpuOnlineStatus = 1;
27
28 // The base of the path to the files and directories which contain CPU data in
29 // the sysfs.
30 const char kCpuDataPathBase[] = "/sys/devices/system/cpu";
31
32 // Suffix of the path to the file listing the range of possible CPUs on the
33 // system.
34 const char kPossibleCpuPathSuffix[] = "/possible";
35
36 // Format of the suffix of the path to the file which contains information
37 // about a particular CPU being online or offline.
38 const char kCpuOnlinePathSuffixFormat[] = "/cpu%d/online";
39
40 // Format of the suffix of the path to the file which contains freq state
41 // information of a CPU.
42 const char kCpuFreqTimeInStatePathSuffixFormat[] =
43 "/cpu%d/cpufreq/stats/time_in_state";
44
45 // Format of the suffix of the path to the directory which contains information
46 // about an idle state of a CPU on the system.
47 const char kCpuIdleStateDirPathSuffixFormat[] = "/cpu%d/cpuidle/state%d";
48
49 // Format of the suffix of the path to the file which contains the name of an
50 // idle state of a CPU.
51 const char kCpuIdleStateNamePathSuffixFormat[] = "/cpu%d/cpuidle/state%d/name";
52
53 // Format of the suffix of the path which contains information about time spent
54 // in an idle state on a CPU.
55 const char kCpuIdleStateTimePathSuffixFormat[] = "/cpu%d/cpuidle/state%d/time";
56
57 // Returns the index at which |str| is in |vector|. If |str| is not present in
58 // |vector|, then it is added to it before its index is returned.
59 int IndexInVector(const std::string& str,
60 std::vector<std::string>* vector) {
61 for (size_t i = 0; i < vector->size(); ++i) {
62 if (str == (*vector)[i])
63 return static_cast<int>(i);
64 }
65
66 // If this is reached, then it means |str| is not present in vector. Add it.
67 vector->push_back(str);
68 return static_cast<int>(vector->size()) - 1;
69 }
70
71 // Returns true if the |i|-th CPU is online; false otherwise.
72 bool CpuIsOnline(const int i) {
73 const std::string online_file_format = base::StringPrintf(
74 "%s%s", kCpuDataPathBase, kCpuOnlinePathSuffixFormat);
75 const std::string cpu_online_file = base::StringPrintf(
76 online_file_format.c_str(), i);
77 if (!base::PathExists(base::FilePath(cpu_online_file))) {
Daniel Erat 2014/03/06 00:46:40 is it worth checking that the cpu directory at lea
Siva Chandra 2014/03/06 17:56:33 This function is called only if sysfs reports that
78 // If the 'online' status file is missing, then it means that the CPU is
79 // not hot-pluggable and hence is always online.
80 return true;
81 }
82
83 int online;
84 std::string cpu_online_string;
85 if (base::ReadFileToString(base::FilePath(cpu_online_file),
86 &cpu_online_string)) {
87 base::TrimString(cpu_online_string, " \n", &cpu_online_string);
Daniel Erat 2014/03/06 00:46:40 i think you can just use: base::TrimWhitespace(
Siva Chandra 2014/03/06 17:56:33 Done.
88 if (base::StringToInt(cpu_online_string, &online))
89 return online == kCpuOnlineStatus;
90 }
91
92 LOG(ERROR) << "Bad format or error reading " << cpu_online_file << ". "
93 << "Assuming offline.";
94 return false;
95 }
96
97 // Samples the CPU idle state information from sysfs. |cpu_count| is the number
98 // of possible CPUs on the system. Sample at index i in |idle_samples|
99 // corresponds to the idle state information of the i-th CPU.
100 void SampleCpuIdleData(
101 int cpu_count,
102 std::vector<std::string>* cpu_idle_state_names,
103 std::vector<CpuDataCollector::StateOccupancySample>* idle_samples) {
104 base::Time start_time = base::Time::Now();
105 for (int cpu = 0; cpu < cpu_count; ++cpu) {
106 CpuDataCollector::StateOccupancySample idle_sample;
107 idle_sample.time = base::Time::Now();
108
109 if (!CpuIsOnline(cpu)) {
110 idle_sample.cpu_online = false;
111 } else {
112 idle_sample.cpu_online = true;
113
114 const std::string idle_state_dir_format = base::StringPrintf(
115 "%s%s", kCpuDataPathBase, kCpuIdleStateDirPathSuffixFormat);
116 for (int state_count = 0; ; ++state_count) {
117 std::string idle_state_dir = base::StringPrintf(
118 idle_state_dir_format.c_str(), cpu, state_count);
119 if (!base::DirectoryExists(base::FilePath(idle_state_dir)))
120 break;
121
122 const std::string name_file_format = base::StringPrintf(
123 "%s%s", kCpuDataPathBase, kCpuIdleStateNamePathSuffixFormat);
124 const std::string name_file_path = base::StringPrintf(
125 name_file_format.c_str(), cpu, state_count);
126 DCHECK(base::PathExists(base::FilePath(name_file_path)));
127
128 const std::string time_file_format = base::StringPrintf(
129 "%s%s", kCpuDataPathBase, kCpuIdleStateTimePathSuffixFormat);
130 const std::string time_file_path = base::StringPrintf(
131 time_file_format.c_str(), cpu, state_count);
132 DCHECK(base::PathExists(base::FilePath(time_file_path)));
133
134 std::string state_name, occupancy_time_string;
135 int64 occupancy_time;
136 if (!base::ReadFileToString(base::FilePath(name_file_path),
137 &state_name) ||
138 !base::ReadFileToString(base::FilePath(time_file_path),
139 &occupancy_time_string)) {
140 // If an error occurs reading/parsing single state data, drop all the
141 // samples as an incomplete sample can mislead consumers of this
142 // sample.
143 LOG(ERROR) << "Error reading idle state from "
144 << idle_state_dir << ". Dropping sample.";
145 idle_samples->clear();
146 return;
147 }
148
149 base::TrimString(state_name, " \n", &state_name);
Daniel Erat 2014/03/06 00:46:40 base::TrimWhitespace()
Siva Chandra 2014/03/06 17:56:33 Done.
150 base::TrimString(
151 occupancy_time_string, " \n", &occupancy_time_string);
Daniel Erat 2014/03/06 00:46:40 base::TrimWhitespace()
Siva Chandra 2014/03/06 17:56:33 Done.
152 if (base::StringToInt64(occupancy_time_string, &occupancy_time)) {
153 // idle state occupancy time in sysfs is recorded in microseconds.
154 int64 time_in_state = occupancy_time/1000;
Daniel Erat 2014/03/06 00:46:40 nit: add spaces to either side of '/'
Siva Chandra 2014/03/06 17:56:33 Done.
155 int index = IndexInVector(state_name, cpu_idle_state_names);
156 idle_sample.time_in_state[index] = time_in_state;
157 } else {
158 LOG(ERROR) << "Bad format in " << time_file_path << ". "
159 << "Dropping sample.";
160 idle_samples->clear();
161 return;
162 }
163 }
164 }
165
166 idle_samples->push_back(idle_sample);
167 }
168
169 // If there was an interruption in sampling (like system suspended),
170 // discard the samples!
171 int64 delay =
172 base::TimeDelta(base::Time::Now() - start_time).InMilliseconds();
173 if (delay > 500) {
Daniel Erat 2014/03/06 00:46:40 nit: move 500 to a constant at the top of the file
Siva Chandra 2014/03/06 17:56:33 Done.
174 idle_samples->clear();
175 LOG(WARNING) << "Dropped an idle state sample due to excessive time delay: "
176 << delay << "milliseconds.";
177 }
178 }
179
180 // Samples the CPU freq state information from sysfs. |cpu_count| is the number
181 // of possible CPUs on the system. Sample at index i in |freq_samples|
182 // corresponds to the freq state information of the i-th CPU.
183 void SampleCpuFreqData(
184 int cpu_count,
185 std::vector<std::string>* cpu_freq_state_names,
186 std::vector<CpuDataCollector::StateOccupancySample>* freq_samples) {
187 base::Time start_time = base::Time::Now();
188 for (int cpu = 0; cpu < cpu_count; ++cpu) {
189 CpuDataCollector::StateOccupancySample freq_sample;
190
191 if (!CpuIsOnline(cpu)) {
192 freq_sample.time = base::Time::Now();
193 freq_sample.cpu_online = false;
194 } else {
195 freq_sample.cpu_online = true;
196
197 const std::string time_in_state_path_format = base::StringPrintf(
198 "%s%s", kCpuDataPathBase, kCpuFreqTimeInStatePathSuffixFormat);
199 const std::string time_in_state_path = base::StringPrintf(
200 time_in_state_path_format.c_str(), cpu);
201 DCHECK(base::PathExists(base::FilePath(time_in_state_path)));
202
203 std::string time_in_state_string;
204 // Note time as close to reading the file as possible. This is not
205 // possible for idle state samples as the information for each state there
206 // is recorded in different files.
207 base::Time now = base::Time::Now();
208 if (!base::ReadFileToString(base::FilePath(time_in_state_path),
209 &time_in_state_string)) {
210 LOG(ERROR) << "Error reading " << time_in_state_path << ". "
211 << "Dropping sample.";
212 freq_samples->clear();
213 return;
214 }
215
216 freq_sample.time = now;
217
218 std::vector<std::string> lines;
219 base::SplitString(time_in_state_string, '\n', &lines);
220 // The last line could end with '\n'. Ignore the last empty string in
221 // such cases.
222 size_t state_count = lines.size();
223 if (state_count > 0 && lines.back().empty())
224 state_count -= 1;
225 for (size_t state = 0; state < state_count; ++state) {
226 std::vector<std::string> pair;
227 int freq_in_khz;
228 int64 occupancy_time;
229
230 // Occupancy of each state is in the format "<state> <time>"
231 base::SplitString(lines[state], ' ', &pair);
232 for (size_t s = 0; s < pair.size(); ++s)
233 base::TrimString(pair[s], " \n", &pair[s]);
Daniel Erat 2014/03/06 00:46:40 base::TrimWhitespace()
Siva Chandra 2014/03/06 17:56:33 Done.
234 if (pair.size() == 2 &&
235 base::StringToInt(pair[0], &freq_in_khz) &&
236 base::StringToInt64(pair[1], &occupancy_time)) {
237 const std::string state_name = base::IntToString(freq_in_khz / 1000);
238 int index = IndexInVector(state_name, cpu_freq_state_names);
239 freq_sample.time_in_state[index] = occupancy_time * 10;
240 } else {
241 LOG(ERROR) << "Bad format in " << time_in_state_path << ". "
242 << "Dropping sample.";
243 freq_samples->clear();
244 return;
245 }
246 }
247 }
248
249 freq_samples->push_back(freq_sample);
250 }
251
252 // If there was an interruption in sampling (like system suspended),
253 // discard the samples!
254 int64 delay =
255 base::TimeDelta(base::Time::Now() - start_time).InMilliseconds();
256 if (delay > 500) {
Daniel Erat 2014/03/06 00:46:40 use a constant here too
Siva Chandra 2014/03/06 17:56:33 Done.
257 freq_samples->clear();
258 LOG(WARNING) << "Dropped a freq state sample due to excessive time delay: "
259 << delay << "milliseconds.";
260 }
261 }
262
263 } // namespace
264
265 CpuDataCollector::CpuDataCollector() : cpu_count_(1), weak_ptr_factory_(this) {
266 const std::string possible_cpu_path = base::StringPrintf(
267 "%s%s", kCpuDataPathBase, kPossibleCpuPathSuffix);
268 if (!base::PathExists(base::FilePath(possible_cpu_path))) {
Daniel Erat 2014/03/06 00:46:40 you probably shouldn't be doing this file access f
Siva Chandra 2014/03/06 17:56:33 Moved to SampleCpuStateOnBlockingPool.
269 LOG(ERROR) << "File listing possible CPUs missing. "
270 << "Defaulting CPU count to 1.";
271 } else {
272 std::string possible_string;
273 if (base::ReadFileToString(base::FilePath(possible_cpu_path),
274 &possible_string)) {
275 int max_cpu;
276 // The possible CPUs are listed in the format "0-N". Hence, N is present
277 // in the substring starting at offset 2.
278 base::TrimString(possible_string, " \n", &possible_string);
279 if (possible_string.find("-") != std::string::npos &&
280 possible_string.length() > 2 &&
281 base::StringToInt(possible_string.substr(2), &max_cpu)) {
282 cpu_count_ = max_cpu + 1;
283 } else {
284 LOG(ERROR) << "Unknown format in the file listing possible CPUs. "
285 << "Defaulting CPU count to 1.";
286 }
287 } else {
288 LOG(ERROR) << "Error reading the file listing possible CPUs. "
289 << "Defaulting CPU count to 1.";
290 }
291 }
292
293 // Initialize the deques in the data vectors.
294 cpu_idle_state_data_.resize(cpu_count_);
295 cpu_freq_state_data_.resize(cpu_count_);
296 }
297
298 void CpuDataCollector::Start() {
299 timer_.Start(FROM_HERE,
300 base::TimeDelta::FromSeconds(kCpuDataSamplePeriodSec),
301 this,
302 &CpuDataCollector::PostSampleCpuState);
303 }
304
305 void CpuDataCollector::PostSampleCpuState() {
306 std::vector<StateOccupancySample>* idle_samples =
307 new std::vector<StateOccupancySample>;
308 std::vector<StateOccupancySample>* freq_samples =
309 new std::vector<StateOccupancySample>;
310 content::BrowserThread::PostBlockingPoolTaskAndReply(
311 FROM_HERE,
312 base::Bind(&CpuDataCollector::SampleCpuStateOnBlockingPool,
313 weak_ptr_factory_.GetWeakPtr(),
314 base::Unretained(idle_samples),
315 base::Unretained(freq_samples)),
316 base::Bind(&CpuDataCollector::SaveCpuStateSamplesOnUIThread,
317 weak_ptr_factory_.GetWeakPtr(),
318 base::Owned(idle_samples),
319 base::Owned(freq_samples)));
320 }
321
322 void CpuDataCollector::SampleCpuStateOnBlockingPool(
323 std::vector<CpuDataCollector::StateOccupancySample>* idle_samples,
324 std::vector<CpuDataCollector::StateOccupancySample>* freq_samples) {
325 DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
326
327 SampleCpuIdleData(cpu_count_, &cpu_idle_state_names_, idle_samples);
Daniel Erat 2014/03/06 00:46:40 i think that you're now accessing *_state_names_ f
Siva Chandra 2014/03/06 17:56:33 Done.
328 SampleCpuFreqData(cpu_count_, &cpu_freq_state_names_, freq_samples);
329 }
330
331 void CpuDataCollector::SaveCpuStateSamplesOnUIThread(
332 const std::vector<CpuDataCollector::StateOccupancySample>* idle_samples,
333 const std::vector<CpuDataCollector::StateOccupancySample>* freq_samples) {
334 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
335
336 // |idle_samples| or |freq_samples| could be empty sometimes (for example, if
337 // sampling was interrupted due to system suspension). Iff they are not empty,
338 // they will have one sample each for each of the CPUs.
339
340 if (idle_samples->size() > 0) {
341 DCHECK_EQ(idle_samples->size(), cpu_idle_state_data_.size());
342 for (size_t i = 0; i < cpu_idle_state_data_.size(); ++i)
343 AddSample(&cpu_idle_state_data_[i], (*idle_samples)[i]);
344 }
345
346 if (freq_samples->size() > 0) {
347 DCHECK_EQ(freq_samples->size(), cpu_freq_state_data_.size());
348 for (size_t i = 0; i < cpu_freq_state_data_.size(); ++i)
349 AddSample(&cpu_freq_state_data_[i], (*freq_samples)[i]);
350 }
351 }
352
353 CpuDataCollector::StateOccupancySample::StateOccupancySample()
354 : cpu_online(false) {
355 }
356
357 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698