blob: 1161f6c84bb984681bbe2068f4fbf197de0948ab [file] [log] [blame]
Darin Petkov8032dd02011-05-09 16:33:19 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Darin Petkov65b01462010-04-14 13:32:20 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "metrics_daemon.h"
Darin Petkov65b01462010-04-14 13:32:20 -07006
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -08007#include <fcntl.h>
Luigi Semenzato8accd332011-05-17 16:37:18 -07008#include <math.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -07009#include <string.h>
Luigi Semenzato8accd332011-05-17 16:37:18 -070010#include <time.h>
Darin Petkov65b01462010-04-14 13:32:20 -070011
Darin Petkov38d5cb02010-06-24 12:10:26 -070012#include <base/file_util.h>
Darin Petkov65b01462010-04-14 13:32:20 -070013#include <base/logging.h>
Luigi Semenzatofb3a8212013-05-07 16:55:00 -070014#include <base/string_number_conversions.h>
Luigi Semenzato29c7ef92011-04-12 14:12:35 -070015#include <base/string_util.h>
Sonny Rao4b8aebb2013-07-31 23:18:31 -070016#include <base/string_split.h>
Mike Frysinger71ebf982012-03-07 10:35:29 -050017#include <base/stringprintf.h>
Darin Petkov40f25732013-04-29 15:07:31 +020018#include <chromeos/dbus/service_constants.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070019#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070020
Darin Petkovf1e85e42010-06-10 15:59:53 -070021#include "counter.h"
22
Darin Petkovf27f0362010-06-04 13:14:19 -070023using base::Time;
24using base::TimeDelta;
25using base::TimeTicks;
Luigi Semenzato8accd332011-05-17 16:37:18 -070026using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070027using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070028using std::vector;
29
Darin Petkovf27f0362010-06-04 13:14:19 -070030
Darin Petkov703ec972010-04-27 11:02:18 -070031#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov40f25732013-04-29 15:07:31 +020032
33static const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
34static const char kCrashReporterUserCrashSignal[] = "UserCrash";
Darin Petkov41e06232010-05-03 16:45:37 -070035
Darin Petkov41e06232010-05-03 16:45:37 -070036static const int kSecondsPerMinute = 60;
37static const int kMinutesPerHour = 60;
38static const int kHoursPerDay = 24;
39static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070040static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
41static const int kDaysPerWeek = 7;
42static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070043
44// The daily use monitor is scheduled to a 1-minute interval after
45// initial user activity and then it's exponentially backed off to
46// 10-minute intervals. Although not required, the back off is
47// implemented because the histogram buckets are spaced exponentially
48// anyway and to avoid too frequent metrics daemon process wake-ups
49// and file I/O.
50static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
51static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070052
Ken Mixterccd84c02010-08-16 19:57:13 -070053const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
54static const char kUncleanShutdownDetectedFile[] =
55 "/tmp/unclean-shutdown-detected";
56
Ken Mixter4c5daa42010-08-26 18:35:06 -070057// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070058const char MetricsDaemon::kMetricDailyUseTimeName[] =
59 "Logging.DailyUseTime";
60const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
61const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
62const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
63
Ken Mixterccd84c02010-08-16 19:57:13 -070064// crash interval metrics
65const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
66 "Logging.KernelCrashInterval";
67const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
68 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070069const char MetricsDaemon::kMetricUserCrashIntervalName[] =
70 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070071
72const int MetricsDaemon::kMetricCrashIntervalMin = 1;
73const int MetricsDaemon::kMetricCrashIntervalMax =
74 4 * kSecondsPerWeek;
75const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
76
77// crash frequency metrics
78const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
79 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070080const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
81 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070082const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
83 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070084const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
85 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070086const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
87 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070088const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
89 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070090const char MetricsDaemon::kMetricUserCrashesDailyName[] =
91 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070092const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
93 "Logging.UserCrashesWeekly";
94const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
95const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
96const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070097
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080098// disk stats metrics
99
100// The {Read,Write}Sectors numbers are in sectors/second.
101// A sector is usually 512 bytes.
102
103const char MetricsDaemon::kMetricReadSectorsLongName[] =
104 "Platform.ReadSectorsLong";
105const char MetricsDaemon::kMetricWriteSectorsLongName[] =
106 "Platform.WriteSectorsLong";
107const char MetricsDaemon::kMetricReadSectorsShortName[] =
108 "Platform.ReadSectorsShort";
109const char MetricsDaemon::kMetricWriteSectorsShortName[] =
110 "Platform.WriteSectorsShort";
111
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700112const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
113const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800114
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700115const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
116
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800117// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
118// sectors.
119const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
120const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700121// Page size is 4k, sector size is 0.5k. We're not interested in page fault
122// rates that the disk cannot sustain.
123const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
124const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
125
126// Major page faults, i.e. the ones that require data to be read from disk.
127
128const char MetricsDaemon::kMetricPageFaultsLongName[] =
129 "Platform.PageFaultsLong";
130const char MetricsDaemon::kMetricPageFaultsShortName[] =
131 "Platform.PageFaultsShort";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800132
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700133// Swap in and Swap out
134
135const char MetricsDaemon::kMetricSwapInLongName[] =
136 "Platform.SwapInLong";
137const char MetricsDaemon::kMetricSwapInShortName[] =
138 "Platform.SwapInShort";
139
140const char MetricsDaemon::kMetricSwapOutLongName[] =
141 "Platform.SwapOutLong";
142const char MetricsDaemon::kMetricSwapOutShortName[] =
143 "Platform.SwapOutShort";
144
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700145// Thermal CPU throttling.
146
147const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
148 "Platform.CpuFrequencyThermalScaling";
149
Ken Mixter4c5daa42010-08-26 18:35:06 -0700150// persistent metrics path
151const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700152
Darin Petkov703ec972010-04-27 11:02:18 -0700153// static
Darin Petkov41e06232010-05-03 16:45:37 -0700154const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700155#define STATE(name, capname) #name,
156#include "power_states.h"
157};
158
Darin Petkov41e06232010-05-03 16:45:37 -0700159// static
Darin Petkov41e06232010-05-03 16:45:37 -0700160const char* MetricsDaemon::kSessionStates_[] = {
161#define STATE(name, capname) #name,
162#include "session_states.h"
163};
164
Luigi Semenzato8accd332011-05-17 16:37:18 -0700165// Memory use stats collection intervals. We collect some memory use interval
166// at these intervals after boot, and we stop collecting after the last one,
167// with the assumption that in most cases the memory use won't change much
168// after that.
169static const int kMemuseIntervals[] = {
170 1 * kSecondsPerMinute, // 1 minute mark
171 4 * kSecondsPerMinute, // 5 minute mark
172 25 * kSecondsPerMinute, // 0.5 hour mark
173 120 * kSecondsPerMinute, // 2.5 hour mark
174 600 * kSecondsPerMinute, // 12.5 hour mark
175};
176
Darin Petkovf1e85e42010-06-10 15:59:53 -0700177MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700178 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700179 session_state_(kUnknownSessionState),
180 user_active_(false),
181 usemon_interval_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700182 usemon_source_(NULL),
183 memuse_initial_time_(0),
184 memuse_interval_index_(0),
185 read_sectors_(0),
186 write_sectors_(0),
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700187 vmstats_(),
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700188 stats_state_(kStatsShort),
189 stats_initial_time_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700190
Ken Mixter4c5daa42010-08-26 18:35:06 -0700191MetricsDaemon::~MetricsDaemon() {
192 DeleteFrequencyCounters();
193}
194
Luigi Semenzato8accd332011-05-17 16:37:18 -0700195double MetricsDaemon::GetActiveTime() {
196 struct timespec ts;
197 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
198 if (r < 0) {
199 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
200 return 0;
201 } else {
202 return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000);
203 }
204}
205
Ken Mixter4c5daa42010-08-26 18:35:06 -0700206void MetricsDaemon::DeleteFrequencyCounters() {
207 for (FrequencyCounters::iterator i = frequency_counters_.begin();
208 i != frequency_counters_.end(); ++i) {
209 delete i->second;
210 i->second = NULL;
211 }
212}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700213
Darin Petkov2ccef012010-05-05 16:06:37 -0700214void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700215 if (run_as_daemon && daemon(0, 0) != 0)
216 return;
217
Ken Mixterccd84c02010-08-16 19:57:13 -0700218 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
219 ProcessKernelCrash();
220 }
221
222 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
223 ProcessUncleanShutdown();
224 }
225
Darin Petkov38d5cb02010-06-24 12:10:26 -0700226 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700227}
228
Ken Mixter4c5daa42010-08-26 18:35:06 -0700229FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
230 return FilePath(kMetricsPath).Append(histogram_name);
231}
232
233void MetricsDaemon::ConfigureCrashIntervalReporter(
234 const char* histogram_name,
235 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
236 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
237 FilePath file_path = GetHistogramPath(histogram_name);
238 (*reporter)->Init(file_path.value().c_str(),
239 histogram_name,
240 kMetricCrashIntervalMin,
241 kMetricCrashIntervalMax,
242 kMetricCrashIntervalBuckets);
243}
244
245void MetricsDaemon::ConfigureCrashFrequencyReporter(
246 const char* histogram_name) {
247 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
248 new chromeos_metrics::TaggedCounterReporter());
249 FilePath file_path = GetHistogramPath(histogram_name);
250 reporter->Init(file_path.value().c_str(),
251 histogram_name,
252 kMetricCrashFrequencyMin,
253 kMetricCrashFrequencyMax,
254 kMetricCrashFrequencyBuckets);
255 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
256 new chromeos_metrics::FrequencyCounter());
257 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
258 chromeos_metrics::kSecondsPerWeek :
259 chromeos_metrics::kSecondsPerDay;
260 new_counter->Init(
261 static_cast<chromeos_metrics::TaggedCounterInterface*>(
262 reporter.release()),
263 cycle_duration);
264 frequency_counters_[histogram_name] = new_counter.release();
265}
266
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800267void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700268 const string& diskstats_path,
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700269 const string& vmstats_path,
270 const string& scaling_max_freq_path,
271 const string& cpuinfo_max_freq_path
272 ) {
Darin Petkov65b01462010-04-14 13:32:20 -0700273 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700274 DCHECK(metrics_lib != NULL);
275 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700276 chromeos_metrics::TaggedCounterReporter::
277 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700278
279 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700280 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700281 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700282
Ken Mixter4c5daa42010-08-26 18:35:06 -0700283 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
284 &kernel_crash_interval_);
285 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
286 &unclean_shutdown_interval_);
287 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
288 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700289
Ken Mixter4c5daa42010-08-26 18:35:06 -0700290 DeleteFrequencyCounters();
291 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700292 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
293 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
294 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
295 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
296 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
297 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
298 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700299
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700300 diskstats_path_ = diskstats_path;
301 vmstats_path_ = vmstats_path;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700302 scaling_max_freq_path_ = scaling_max_freq_path;
303 cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700304 StatsReporterInit();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800305
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700306 // Start collecting meminfo stats.
307 ScheduleMeminfoCallback(kMetricMeminfoInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700308 ScheduleMemuseCallback(true, 0);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700309
Darin Petkov2ccef012010-05-05 16:06:37 -0700310 // Don't setup D-Bus and GLib in test mode.
311 if (testing)
312 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700313
Darin Petkov703ec972010-04-27 11:02:18 -0700314 g_type_init();
Ben Chan6f598422013-06-22 06:29:36 -0700315 dbus_threads_init_default();
Darin Petkov65b01462010-04-14 13:32:20 -0700316
Darin Petkov703ec972010-04-27 11:02:18 -0700317 DBusError error;
318 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700319
David James3b3add52010-06-04 15:01:19 -0700320 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700321 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
322 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700323
Darin Petkov703ec972010-04-27 11:02:18 -0700324 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700325
Darin Petkov40f25732013-04-29 15:07:31 +0200326 vector<string> matches;
327 matches.push_back(
328 StringPrintf("type='signal',interface='%s',path='/',member='%s'",
329 kCrashReporterInterface,
330 kCrashReporterUserCrashSignal));
331 matches.push_back(
332 StringPrintf("type='signal',interface='%s',path='%s',member='%s'",
333 power_manager::kPowerManagerInterface,
334 power_manager::kPowerManagerServicePath,
335 power_manager::kPowerStateChangedSignal));
336 matches.push_back(
337 StringPrintf("type='signal',sender='%s',interface='%s',path='%s'",
338 login_manager::kSessionManagerServiceName,
339 login_manager::kSessionManagerInterface,
340 login_manager::kSessionManagerServicePath));
341
Darin Petkov703ec972010-04-27 11:02:18 -0700342 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov40f25732013-04-29 15:07:31 +0200343 for (vector<string>::const_iterator it = matches.begin();
344 it != matches.end(); ++it) {
345 const char* match = it->c_str();
Darin Petkov41e06232010-05-03 16:45:37 -0700346 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700347 dbus_bus_add_match(connection, match, &error);
348 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
349 "unable to add a match: " << SAFE_MESSAGE(error);
350 }
351
352 // Adds the D-Bus filter routine to be called back whenever one of
353 // the registered D-Bus matches is successful. The daemon is not
354 // activated for D-Bus messages that don't match.
355 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700356}
357
358void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700359 GMainLoop* loop = g_main_loop_new(NULL, false);
360 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700361}
362
Darin Petkov703ec972010-04-27 11:02:18 -0700363// static
364DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
365 DBusMessage* message,
366 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700367 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700368 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700369
370 int message_type = dbus_message_get_type(message);
371 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700372 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700373 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
374 }
375
376 // Signal messages always have interfaces.
377 const char* interface = dbus_message_get_interface(message);
378 CHECK(interface != NULL);
379
380 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
381
382 DBusMessageIter iter;
383 dbus_message_iter_init(message, &iter);
Darin Petkov40f25732013-04-29 15:07:31 +0200384 if (strcmp(interface, kCrashReporterInterface) == 0) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700385 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200386 kCrashReporterUserCrashSignal) == 0);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700387 daemon->ProcessUserCrash();
Darin Petkov40f25732013-04-29 15:07:31 +0200388 } else if (strcmp(interface, power_manager::kPowerManagerInterface) == 0) {
Darin Petkov41e06232010-05-03 16:45:37 -0700389 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200390 power_manager::kPowerStateChangedSignal) == 0);
David James3b3add52010-06-04 15:01:19 -0700391 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700392 dbus_message_iter_get_basic(&iter, &state_name);
Darin Petkov40f25732013-04-29 15:07:31 +0200393 daemon->PowerStateChanged(state_name, now);
394 } else if (strcmp(interface, login_manager::kSessionManagerInterface) == 0) {
395 const char* member = dbus_message_get_member(message);
396 if (strcmp(member, login_manager::kScreenIsLockedSignal) == 0) {
397 daemon->SetUserActiveState(false, now);
398 } else if (strcmp(member, login_manager::kScreenIsUnlockedSignal) == 0) {
399 daemon->SetUserActiveState(true, now);
400 } else if (strcmp(member, login_manager::kSessionStateChangedSignal) == 0) {
401 char* state_name;
402 dbus_message_iter_get_basic(&iter, &state_name);
403 daemon->SessionStateChanged(state_name, now);
404 }
Darin Petkov703ec972010-04-27 11:02:18 -0700405 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700406 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700407 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
408 }
409
410 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700411}
412
Darin Petkovf27f0362010-06-04 13:14:19 -0700413void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700414 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700415 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700416
417 if (power_state_ != kPowerStateOn)
418 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700419}
420
421MetricsDaemon::PowerState
422MetricsDaemon::LookupPowerState(const char* state_name) {
423 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700424 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700425 return static_cast<PowerState>(i);
426 }
427 }
Darin Petkov41e06232010-05-03 16:45:37 -0700428 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700429 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700430}
431
Darin Petkovf27f0362010-06-04 13:14:19 -0700432void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700433 DLOG(INFO) << "user session state: " << state_name;
434 session_state_ = LookupSessionState(state_name);
435 SetUserActiveState(session_state_ == kSessionStateStarted, now);
436}
437
438MetricsDaemon::SessionState
439MetricsDaemon::LookupSessionState(const char* state_name) {
440 for (int i = 0; i < kNumberSessionStates; i++) {
441 if (strcmp(state_name, kSessionStates_[i]) == 0) {
442 return static_cast<SessionState>(i);
443 }
444 }
445 DLOG(WARNING) << "unknown user session state: " << state_name;
446 return kUnknownSessionState;
447}
448
Darin Petkovf27f0362010-06-04 13:14:19 -0700449void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700450 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
451
452 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700453 // the day since Epoch, and logs the usage data. Guards against the
454 // time jumping back and forth due to the user changing it by
455 // discarding the new use time.
456 int seconds = 0;
457 if (user_active_ && now > user_active_last_) {
458 TimeDelta since_active = now - user_active_last_;
459 if (since_active < TimeDelta::FromSeconds(
460 kUseMonitorIntervalMax + kSecondsPerMinute)) {
461 seconds = static_cast<int>(since_active.InSeconds());
462 }
463 }
464 TimeDelta since_epoch = now - Time();
465 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700466 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700467 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700468 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700469
Ken Mixter4c5daa42010-08-26 18:35:06 -0700470 // Flush finished cycles of all frequency counters.
471 for (FrequencyCounters::iterator i = frequency_counters_.begin();
472 i != frequency_counters_.end(); ++i) {
473 i->second->FlushFinishedCycles();
474 }
475
Darin Petkov41e06232010-05-03 16:45:37 -0700476 // Schedules a use monitor on inactive->active transitions and
477 // unschedules it on active->inactive transitions.
478 if (!user_active_ && active)
479 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
480 else if (user_active_ && !active)
481 UnscheduleUseMonitor();
482
483 // Remembers the current active state and the time of the last
484 // activity update.
485 user_active_ = active;
486 user_active_last_ = now;
487}
488
Darin Petkov1bb904e2010-06-16 15:58:06 -0700489void MetricsDaemon::ProcessUserCrash() {
490 // Counts the active use time up to now.
491 SetUserActiveState(user_active_, Time::Now());
492
493 // Reports the active use time since the last crash and resets it.
494 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700495
Ken Mixter4c5daa42010-08-26 18:35:06 -0700496 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
497 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
498 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
499 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700500}
501
Darin Petkov38d5cb02010-06-24 12:10:26 -0700502void MetricsDaemon::ProcessKernelCrash() {
503 // Counts the active use time up to now.
504 SetUserActiveState(user_active_, Time::Now());
505
506 // Reports the active use time since the last crash and resets it.
507 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700508
Ken Mixter4c5daa42010-08-26 18:35:06 -0700509 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
510 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
511 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
512 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700513}
514
Ken Mixterccd84c02010-08-16 19:57:13 -0700515void MetricsDaemon::ProcessUncleanShutdown() {
516 // Counts the active use time up to now.
517 SetUserActiveState(user_active_, Time::Now());
518
519 // Reports the active use time since the last crash and resets it.
520 unclean_shutdown_interval_->Flush();
521
Ken Mixter4c5daa42010-08-26 18:35:06 -0700522 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
523 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
524 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
525 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700526}
527
Luigi Semenzato8accd332011-05-17 16:37:18 -0700528bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700529 FilePath crash_detected(crash_file);
530 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700531 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700532
533 // Deletes the crash-detected file so that the daemon doesn't report
534 // another kernel crash in case it's restarted.
535 file_util::Delete(crash_detected,
536 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700537 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700538}
539
Darin Petkov41e06232010-05-03 16:45:37 -0700540// static
541gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
542 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
543}
544
545bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700546 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700547
548 // If a new monitor source/instance is scheduled, returns false to
549 // tell GLib to destroy this monitor source/instance. Returns true
550 // otherwise to keep calling back this monitor.
551 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
552}
553
554bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
555{
Darin Petkov2ccef012010-05-05 16:06:37 -0700556 if (testing_)
557 return false;
558
Darin Petkov41e06232010-05-03 16:45:37 -0700559 // Caps the interval -- the bigger the interval, the more active use
560 // time will be potentially dropped on system shutdown.
561 if (interval > kUseMonitorIntervalMax)
562 interval = kUseMonitorIntervalMax;
563
564 if (backoff) {
565 // Back-off mode is used by the use monitor to reschedule itself
566 // with exponential back-off in time. This mode doesn't create a
567 // new timeout source if the new interval is the same as the old
568 // one. Also, if a new timeout source is created, the old one is
569 // not destroyed explicitly here -- it will be destroyed by GLib
570 // when the monitor returns FALSE (see UseMonitor and
571 // UseMonitorStatic).
572 if (interval == usemon_interval_)
573 return false;
574 } else {
575 UnscheduleUseMonitor();
576 }
577
578 // Schedules a new use monitor for |interval| seconds from now.
579 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
580 usemon_source_ = g_timeout_source_new_seconds(interval);
581 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
582 NULL); // No destroy notification.
583 g_source_attach(usemon_source_,
584 NULL); // Default context.
585 usemon_interval_ = interval;
586 return true;
587}
588
589void MetricsDaemon::UnscheduleUseMonitor() {
590 // If there's a use monitor scheduled already, destroys it.
591 if (usemon_source_ == NULL)
592 return;
593
594 DLOG(INFO) << "destroying use monitor";
595 g_source_destroy(usemon_source_);
596 usemon_source_ = NULL;
597 usemon_interval_ = 0;
598}
599
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700600void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800601 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700602 VmStatsReadStats(&vmstats_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800603 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700604 stats_state_ = kStatsLong;
605 stats_initial_time_ = GetActiveTime();
606 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700607 LOG(WARNING) << "not collecting disk stats";
608 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700609 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700610 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800611}
612
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700613void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800614 if (testing_) {
615 return;
616 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700617 g_timeout_add_seconds(wait, StatsCallbackStatic, this);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800618}
619
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700620bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800621 long int* write_sectors) {
622 int nchars;
623 int nitems;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700624 bool success = false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800625 char line[200];
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700626 if (diskstats_path_.empty()) {
627 return false;
628 }
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800629 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800630 if (file < 0) {
631 PLOG(WARNING) << "cannot open " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700632 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800633 }
634 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
635 if (nchars < 0) {
636 PLOG(WARNING) << "cannot read from " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700637 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800638 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700639 LOG_IF(WARNING, nchars == sizeof(line))
640 << "line too long in " << diskstats_path_;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800641 line[nchars] = '\0';
642 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
643 read_sectors, write_sectors);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700644 if (nitems == 2) {
645 success = true;
646 } else {
647 LOG(WARNING) << "found " << nitems << " items in "
648 << diskstats_path_ << ", expected 2";
649 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800650 }
651 HANDLE_EINTR(close(file));
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700652 return success;
653}
654
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700655bool MetricsDaemon::VmStatsParseStats(const char* stats,
656 struct VmstatRecord* record) {
657 // a mapping of string name to field in VmstatRecord and whether we found it
658 struct mapping {
659 const string name;
660 uint64_t* value_p;
661 bool found;
662 } map[] =
663 { { .name = "pgmajfault",
664 .value_p = &record->page_faults_,
665 .found = false },
666 { .name = "pswpin",
667 .value_p = &record->swap_in_,
668 .found = false },
669 { .name = "pswpout",
670 .value_p = &record->swap_out_,
671 .found = false }, };
672
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700673 // Each line in the file has the form
674 // <ID> <VALUE>
675 // for instance:
676 // nr_free_pages 213427
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700677 vector<string> lines;
678 Tokenize(stats, "\n", &lines);
679 for (vector<string>::iterator it = lines.begin();
680 it != lines.end(); ++it) {
681 vector<string> tokens;
682 base::SplitString(*it, ' ', &tokens);
683 if (tokens.size() == 2) {
684 for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
685 if (!tokens[0].compare(map[i].name)) {
686 if (!base::StringToUint64(tokens[1], map[i].value_p))
687 return false;
688 map[i].found = true;
689 }
690 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700691 } else {
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700692 LOG(WARNING) << "unexpected vmstat format";
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700693 }
694 }
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700695 // make sure we got all the stats
696 for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
697 if (map[i].found == false) {
698 LOG(WARNING) << "vmstat missing " << map[i].name;
699 return false;
700 }
701 }
702 return true;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700703}
704
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700705bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
706 string value_string;
707 FilePath* path = new FilePath(vmstats_path_);
708 if (!file_util::ReadFileToString(*path, &value_string)) {
709 delete path;
710 LOG(WARNING) << "cannot read " << vmstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700711 return false;
712 }
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700713 delete path;
714 return VmStatsParseStats(value_string.c_str(), stats);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800715}
716
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700717bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
Luigi Semenzatod92d18c2013-06-04 13:24:21 -0700718 const FilePath sysfs_path(sysfs_file_name);
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700719 string value_string;
720 if (!file_util::ReadFileToString(sysfs_path, &value_string)) {
721 LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
722 return false;
723 }
724 if (!RemoveChars(value_string, "\n", &value_string)) {
725 LOG(WARNING) << "no newline in " << value_string;
726 // Continue even though the lack of newline is suspicious.
727 }
728 if (!base::StringToInt(value_string, value)) {
729 LOG(WARNING) << "cannot convert " << value_string << " to int";
730 return false;
731 }
732 return true;
733}
734
735void MetricsDaemon::SendCpuThrottleMetrics() {
736 // |max_freq| is 0 only the first time through.
737 static int max_freq = 0;
738 if (max_freq == -1)
739 // Give up, as sysfs did not report max_freq correctly.
740 return;
741 if (max_freq == 0 || testing_) {
742 // One-time initialization of max_freq. (Every time when testing.)
743 if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
744 max_freq = -1;
745 return;
746 }
747 if (max_freq == 0) {
748 LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
749 max_freq = -1;
750 return;
751 }
752 if (max_freq % 10000 == 1000) {
753 // Special case: system has turbo mode, and max non-turbo frequency is
754 // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
755 // being multiples of (at least) 10 MHz. Although there is no guarantee
756 // of this, it seems a fairly reasonable assumption. Otherwise we should
757 // read scaling_available_frequencies, sort the frequencies, compare the
758 // two highest ones, and check if they differ by 1000 (kHz) (and that's a
759 // hack too, no telling when it will change).
760 max_freq -= 1000;
761 }
762 }
763 int scaled_freq = 0;
764 if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
765 return;
766 // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
767 // scaled_freq is not the actual turbo frequency. We indicate this situation
768 // with a 101% value.
769 int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
770 SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102);
771}
772
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800773// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700774gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
775 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800776 return false; // one-time callback
777}
778
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700779// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700780
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700781void MetricsDaemon::StatsCallback() {
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700782 long int read_sectors_now, write_sectors_now;
783 struct VmstatRecord vmstats_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700784 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700785 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700786 if (testing_) {
787 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700788 delta_time = stats_state_ == kStatsShort ?
789 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700790 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700791 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
792 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700793 int delta_read = read_sectors_now - read_sectors_;
794 int delta_write = write_sectors_now - write_sectors_;
795 int read_sectors_per_second = delta_read / delta_time;
796 int write_sectors_per_second = delta_write / delta_time;
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700797 bool vmstats_success = VmStatsReadStats(&vmstats_now);
798 uint64_t delta_faults = vmstats_now.page_faults_ - vmstats_.page_faults_;
799 uint64_t delta_swap_in = vmstats_now.swap_in_ - vmstats_.swap_in_;
800 uint64_t delta_swap_out = vmstats_now.swap_out_ - vmstats_.swap_out_;
801 uint64_t page_faults_per_second = delta_faults / delta_time;
802 uint64_t swap_in_per_second = delta_swap_in / delta_time;
803 uint64_t swap_out_per_second = delta_swap_out / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800804
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700805 switch (stats_state_) {
806 case kStatsShort:
807 if (diskstats_success) {
808 SendMetric(kMetricReadSectorsShortName,
809 read_sectors_per_second,
810 1,
811 kMetricSectorsIOMax,
812 kMetricSectorsBuckets);
813 SendMetric(kMetricWriteSectorsShortName,
814 write_sectors_per_second,
815 1,
816 kMetricSectorsIOMax,
817 kMetricSectorsBuckets);
818 }
819 if (vmstats_success) {
820 SendMetric(kMetricPageFaultsShortName,
821 page_faults_per_second,
822 1,
823 kMetricPageFaultsMax,
824 kMetricPageFaultsBuckets);
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700825 SendMetric(kMetricSwapInShortName,
826 swap_in_per_second,
827 1,
828 kMetricPageFaultsMax,
829 kMetricPageFaultsBuckets);
830 SendMetric(kMetricSwapOutShortName,
831 swap_out_per_second,
832 1,
833 kMetricPageFaultsMax,
834 kMetricPageFaultsBuckets);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700835 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800836 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700837 stats_state_ = kStatsLong;
838 ScheduleStatsCallback(kMetricStatsLongInterval -
839 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800840 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700841 case kStatsLong:
842 if (diskstats_success) {
843 SendMetric(kMetricReadSectorsLongName,
844 read_sectors_per_second,
845 1,
846 kMetricSectorsIOMax,
847 kMetricSectorsBuckets);
848 SendMetric(kMetricWriteSectorsLongName,
849 write_sectors_per_second,
850 1,
851 kMetricSectorsIOMax,
852 kMetricSectorsBuckets);
853 // Reset sector counters.
854 read_sectors_ = read_sectors_now;
855 write_sectors_ = write_sectors_now;
856 }
857 if (vmstats_success) {
858 SendMetric(kMetricPageFaultsLongName,
859 page_faults_per_second,
860 1,
861 kMetricPageFaultsMax,
862 kMetricPageFaultsBuckets);
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700863 SendMetric(kMetricSwapInLongName,
864 swap_in_per_second,
865 1,
866 kMetricPageFaultsMax,
867 kMetricPageFaultsBuckets);
868 SendMetric(kMetricSwapOutLongName,
869 swap_out_per_second,
870 1,
871 kMetricPageFaultsMax,
872 kMetricPageFaultsBuckets);
873
874 vmstats_ = vmstats_now;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700875 }
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700876 SendCpuThrottleMetrics();
Luigi Semenzato8accd332011-05-17 16:37:18 -0700877 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700878 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800879 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700880 stats_state_ = kStatsShort;
881 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800882 break;
883 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700884 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800885 }
886}
887
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700888void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
889 if (testing_) {
890 return;
891 }
892 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
893}
894
895// static
896gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
897 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
898}
899
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700900bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700901 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700902 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700903 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700904 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
905 return false;
906 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700907 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700908}
909
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700910bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700911 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700912 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
913 { "MemFree", "MemFree" },
914 { "Buffers", "Buffers" },
915 { "Cached", "Cached" },
916 // { "SwapCached", "SwapCached" },
917 { "Active", "Active" },
918 { "Inactive", "Inactive" },
919 { "ActiveAnon", "Active(anon)" },
920 { "InactiveAnon", "Inactive(anon)" },
921 { "ActiveFile" , "Active(file)" },
922 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800923 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700924 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800925 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
926 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700927 // { "Dirty", "Dirty" },
928 // { "Writeback", "Writeback" },
929 { "AnonPages", "AnonPages" },
930 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800931 { "Shmem", "Shmem", kMeminfoOp_HistLog },
932 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700933 // { "SReclaimable", "SReclaimable" },
934 // { "SUnreclaim", "SUnreclaim" },
935 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700936 vector<MeminfoRecord> fields(fields_array,
937 fields_array + arraysize(fields_array));
938 if (!FillMeminfo(meminfo_raw, &fields)) {
939 return false;
940 }
941 int total_memory = fields[0].value;
942 if (total_memory == 0) {
943 // this "cannot happen"
944 LOG(WARNING) << "borked meminfo parser";
945 return false;
946 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800947 int swap_total = 0;
948 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700949 // Send all fields retrieved, except total memory.
950 for (unsigned int i = 1; i < fields.size(); i++) {
951 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800952 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800953 switch (fields[i].op) {
954 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800955 // report value as percent of total memory
956 percent = fields[i].value * 100 / total_memory;
957 SendLinearMetric(metrics_name, percent, 100, 101);
958 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800959 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800960 // report value in kbytes, log scale, 4Gb max
961 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
962 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800963 case kMeminfoOp_SwapTotal:
964 swap_total = fields[i].value;
965 case kMeminfoOp_SwapFree:
966 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800967 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700968 }
969 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800970 if (swap_total > 0) {
971 int swap_used = swap_total - swap_free;
972 int swap_used_percent = swap_used * 100 / swap_total;
973 SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
974 SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
975 100, 101);
976 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700977 return true;
978}
979
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700980bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
981 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700982 vector<string> lines;
983 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700984
985 // Scan meminfo output and collect field values. Each field name has to
986 // match a meminfo entry (case insensitive) after removing non-alpha
987 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700988 unsigned int ifield = 0;
989 for (unsigned int iline = 0;
990 iline < nlines && ifield < fields->size();
991 iline++) {
992 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700993 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700994 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
995 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700996 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700997 (*fields)[ifield].value =
998 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700999 if (*rest != '\0') {
1000 LOG(WARNING) << "missing meminfo value";
1001 return false;
1002 }
Luigi Semenzato8accd332011-05-17 16:37:18 -07001003 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001004 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001005 }
Luigi Semenzato8accd332011-05-17 16:37:18 -07001006 if (ifield < fields->size()) {
1007 // End of input reached while scanning.
1008 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
1009 << " and following";
1010 return false;
1011 }
1012 return true;
1013}
1014
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001015void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -07001016 double time_elapsed) {
1017 if (testing_) {
1018 return;
1019 }
1020 int interval = kMemuseIntervals[memuse_interval_index_];
1021 int wait;
1022 if (new_callback) {
1023 memuse_initial_time_ = GetActiveTime();
1024 wait = interval;
1025 } else {
1026 wait = ceil(interval - time_elapsed); // round up
1027 }
1028 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
1029}
1030
1031// static
1032gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
1033 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
1034 daemon->MemuseCallback();
1035 return false;
1036}
1037
1038void MetricsDaemon::MemuseCallback() {
1039 // Since we only care about active time (i.e. uptime minus sleep time) but
1040 // the callbacks are driven by real time (uptime), we check if we should
1041 // reschedule this callback due to intervening sleep periods.
1042 double now = GetActiveTime();
1043 double active_time = now - memuse_initial_time_;
1044 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
1045 // Not enough active time has passed. Reschedule the callback.
1046 ScheduleMemuseCallback(false, active_time);
1047 } else {
1048 // Enough active time has passed. Do the work, and (if we succeed) see if
1049 // we need to do more.
1050 if (MemuseCallbackWork() &&
1051 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
1052 memuse_interval_index_++;
1053 ScheduleMemuseCallback(true, 0);
1054 }
1055 }
1056}
1057
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001058bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001059 string meminfo_raw;
1060 const FilePath meminfo_path("/proc/meminfo");
1061 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
1062 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
1063 return false;
1064 }
1065 return ProcessMemuse(meminfo_raw);
1066}
1067
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001068bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001069 static const MeminfoRecord fields_array[] = {
1070 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
1071 { "ActiveAnon", "Active(anon)" },
1072 { "InactiveAnon", "Inactive(anon)" },
1073 };
1074 vector<MeminfoRecord> fields(fields_array,
1075 fields_array + arraysize(fields_array));
1076 if (!FillMeminfo(meminfo_raw, &fields)) {
1077 return false;
1078 }
1079 int total = fields[0].value;
1080 int active_anon = fields[1].value;
1081 int inactive_anon = fields[2].value;
1082 if (total == 0) {
1083 // this "cannot happen"
1084 LOG(WARNING) << "borked meminfo parser";
1085 return false;
1086 }
1087 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
1088 memuse_interval_index_);
1089 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
1090 100, 101);
1091 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001092}
1093
Darin Petkovf1e85e42010-06-10 15:59:53 -07001094// static
Ken Mixterccd84c02010-08-16 19:57:13 -07001095void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -07001096 if (count <= 0)
1097 return;
1098
Darin Petkovf1e85e42010-06-10 15:59:53 -07001099 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
1100 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
1101 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
1102 kMetricDailyUseTimeMin,
1103 kMetricDailyUseTimeMax,
1104 kMetricDailyUseTimeBuckets);
1105}
1106
Darin Petkov38d5cb02010-06-24 12:10:26 -07001107void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -07001108 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -07001109 DLOG(INFO) << "received metric: " << name << " " << sample << " "
1110 << min << " " << max << " " << nbuckets;
1111 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -07001112}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001113
1114void MetricsDaemon::SendLinearMetric(const string& name, int sample,
1115 int max, int nbuckets) {
1116 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
1117 << max << " " << nbuckets;
1118 // TODO(semenzato): add a proper linear histogram to the Chrome external
1119 // metrics API.
1120 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1121 metrics_lib_->SendEnumToUMA(name, sample, max);
1122}