blob: e2c4b13fd43f136fed575d3ace11a8393dfa6802 [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>
Mike Frysinger71ebf982012-03-07 10:35:29 -050016#include <base/stringprintf.h>
Darin Petkov40f25732013-04-29 15:07:31 +020017#include <chromeos/dbus/service_constants.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070018#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070019
Darin Petkovf1e85e42010-06-10 15:59:53 -070020#include "counter.h"
21
Darin Petkovf27f0362010-06-04 13:14:19 -070022using base::Time;
23using base::TimeDelta;
24using base::TimeTicks;
Luigi Semenzato8accd332011-05-17 16:37:18 -070025using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070026using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070027using std::vector;
28
Darin Petkovf27f0362010-06-04 13:14:19 -070029
Darin Petkov703ec972010-04-27 11:02:18 -070030#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov40f25732013-04-29 15:07:31 +020031
32static const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
33static const char kCrashReporterUserCrashSignal[] = "UserCrash";
Darin Petkov41e06232010-05-03 16:45:37 -070034
Darin Petkov41e06232010-05-03 16:45:37 -070035static const int kSecondsPerMinute = 60;
36static const int kMinutesPerHour = 60;
37static const int kHoursPerDay = 24;
38static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070039static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
40static const int kDaysPerWeek = 7;
41static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070042
43// The daily use monitor is scheduled to a 1-minute interval after
44// initial user activity and then it's exponentially backed off to
45// 10-minute intervals. Although not required, the back off is
46// implemented because the histogram buckets are spaced exponentially
47// anyway and to avoid too frequent metrics daemon process wake-ups
48// and file I/O.
49static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
50static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070051
Ken Mixterccd84c02010-08-16 19:57:13 -070052const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
53static const char kUncleanShutdownDetectedFile[] =
54 "/tmp/unclean-shutdown-detected";
55
Ken Mixter4c5daa42010-08-26 18:35:06 -070056// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070057const char MetricsDaemon::kMetricDailyUseTimeName[] =
58 "Logging.DailyUseTime";
59const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
60const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
61const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
62
Ken Mixterccd84c02010-08-16 19:57:13 -070063// crash interval metrics
64const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
65 "Logging.KernelCrashInterval";
66const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
67 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070068const char MetricsDaemon::kMetricUserCrashIntervalName[] =
69 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070070
71const int MetricsDaemon::kMetricCrashIntervalMin = 1;
72const int MetricsDaemon::kMetricCrashIntervalMax =
73 4 * kSecondsPerWeek;
74const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
75
76// crash frequency metrics
77const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
78 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070079const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
80 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070081const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
82 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070083const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
84 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070085const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
86 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070087const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
88 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070089const char MetricsDaemon::kMetricUserCrashesDailyName[] =
90 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070091const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
92 "Logging.UserCrashesWeekly";
93const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
94const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
95const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070096
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080097// disk stats metrics
98
99// The {Read,Write}Sectors numbers are in sectors/second.
100// A sector is usually 512 bytes.
101
102const char MetricsDaemon::kMetricReadSectorsLongName[] =
103 "Platform.ReadSectorsLong";
104const char MetricsDaemon::kMetricWriteSectorsLongName[] =
105 "Platform.WriteSectorsLong";
106const char MetricsDaemon::kMetricReadSectorsShortName[] =
107 "Platform.ReadSectorsShort";
108const char MetricsDaemon::kMetricWriteSectorsShortName[] =
109 "Platform.WriteSectorsShort";
110
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700111const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
112const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800113
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700114const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
115
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800116// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
117// sectors.
118const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
119const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700120// Page size is 4k, sector size is 0.5k. We're not interested in page fault
121// rates that the disk cannot sustain.
122const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
123const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
124
125// Major page faults, i.e. the ones that require data to be read from disk.
126
127const char MetricsDaemon::kMetricPageFaultsLongName[] =
128 "Platform.PageFaultsLong";
129const char MetricsDaemon::kMetricPageFaultsShortName[] =
130 "Platform.PageFaultsShort";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800131
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700132// Thermal CPU throttling.
133
134const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
135 "Platform.CpuFrequencyThermalScaling";
136
Ken Mixter4c5daa42010-08-26 18:35:06 -0700137// persistent metrics path
138const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700139
Darin Petkov703ec972010-04-27 11:02:18 -0700140// static
Darin Petkov41e06232010-05-03 16:45:37 -0700141const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700142#define STATE(name, capname) #name,
143#include "power_states.h"
144};
145
Darin Petkov41e06232010-05-03 16:45:37 -0700146// static
Darin Petkov41e06232010-05-03 16:45:37 -0700147const char* MetricsDaemon::kSessionStates_[] = {
148#define STATE(name, capname) #name,
149#include "session_states.h"
150};
151
Luigi Semenzato8accd332011-05-17 16:37:18 -0700152// Memory use stats collection intervals. We collect some memory use interval
153// at these intervals after boot, and we stop collecting after the last one,
154// with the assumption that in most cases the memory use won't change much
155// after that.
156static const int kMemuseIntervals[] = {
157 1 * kSecondsPerMinute, // 1 minute mark
158 4 * kSecondsPerMinute, // 5 minute mark
159 25 * kSecondsPerMinute, // 0.5 hour mark
160 120 * kSecondsPerMinute, // 2.5 hour mark
161 600 * kSecondsPerMinute, // 12.5 hour mark
162};
163
Darin Petkovf1e85e42010-06-10 15:59:53 -0700164MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700165 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700166 session_state_(kUnknownSessionState),
167 user_active_(false),
168 usemon_interval_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700169 usemon_source_(NULL),
170 memuse_initial_time_(0),
171 memuse_interval_index_(0),
172 read_sectors_(0),
173 write_sectors_(0),
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700174 page_faults_(0),
175 stats_state_(kStatsShort),
176 stats_initial_time_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700177
Ken Mixter4c5daa42010-08-26 18:35:06 -0700178MetricsDaemon::~MetricsDaemon() {
179 DeleteFrequencyCounters();
180}
181
Luigi Semenzato8accd332011-05-17 16:37:18 -0700182double MetricsDaemon::GetActiveTime() {
183 struct timespec ts;
184 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
185 if (r < 0) {
186 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
187 return 0;
188 } else {
189 return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000);
190 }
191}
192
Ken Mixter4c5daa42010-08-26 18:35:06 -0700193void MetricsDaemon::DeleteFrequencyCounters() {
194 for (FrequencyCounters::iterator i = frequency_counters_.begin();
195 i != frequency_counters_.end(); ++i) {
196 delete i->second;
197 i->second = NULL;
198 }
199}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700200
Darin Petkov2ccef012010-05-05 16:06:37 -0700201void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700202 if (run_as_daemon && daemon(0, 0) != 0)
203 return;
204
Ken Mixterccd84c02010-08-16 19:57:13 -0700205 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
206 ProcessKernelCrash();
207 }
208
209 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
210 ProcessUncleanShutdown();
211 }
212
Darin Petkov38d5cb02010-06-24 12:10:26 -0700213 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700214}
215
Ken Mixter4c5daa42010-08-26 18:35:06 -0700216FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
217 return FilePath(kMetricsPath).Append(histogram_name);
218}
219
220void MetricsDaemon::ConfigureCrashIntervalReporter(
221 const char* histogram_name,
222 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
223 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
224 FilePath file_path = GetHistogramPath(histogram_name);
225 (*reporter)->Init(file_path.value().c_str(),
226 histogram_name,
227 kMetricCrashIntervalMin,
228 kMetricCrashIntervalMax,
229 kMetricCrashIntervalBuckets);
230}
231
232void MetricsDaemon::ConfigureCrashFrequencyReporter(
233 const char* histogram_name) {
234 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
235 new chromeos_metrics::TaggedCounterReporter());
236 FilePath file_path = GetHistogramPath(histogram_name);
237 reporter->Init(file_path.value().c_str(),
238 histogram_name,
239 kMetricCrashFrequencyMin,
240 kMetricCrashFrequencyMax,
241 kMetricCrashFrequencyBuckets);
242 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
243 new chromeos_metrics::FrequencyCounter());
244 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
245 chromeos_metrics::kSecondsPerWeek :
246 chromeos_metrics::kSecondsPerDay;
247 new_counter->Init(
248 static_cast<chromeos_metrics::TaggedCounterInterface*>(
249 reporter.release()),
250 cycle_duration);
251 frequency_counters_[histogram_name] = new_counter.release();
252}
253
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800254void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700255 const string& diskstats_path,
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700256 const string& vmstats_path,
257 const string& scaling_max_freq_path,
258 const string& cpuinfo_max_freq_path
259 ) {
Darin Petkov65b01462010-04-14 13:32:20 -0700260 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700261 DCHECK(metrics_lib != NULL);
262 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700263 chromeos_metrics::TaggedCounterReporter::
264 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700265
266 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700267 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700268 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700269
Ken Mixter4c5daa42010-08-26 18:35:06 -0700270 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
271 &kernel_crash_interval_);
272 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
273 &unclean_shutdown_interval_);
274 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
275 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700276
Ken Mixter4c5daa42010-08-26 18:35:06 -0700277 DeleteFrequencyCounters();
278 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700279 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
280 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
281 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
282 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
283 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
284 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
285 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700286
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700287 diskstats_path_ = diskstats_path;
288 vmstats_path_ = vmstats_path;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700289 scaling_max_freq_path_ = scaling_max_freq_path;
290 cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700291 StatsReporterInit();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800292
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700293 // Start collecting meminfo stats.
294 ScheduleMeminfoCallback(kMetricMeminfoInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700295 ScheduleMemuseCallback(true, 0);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700296
Darin Petkov2ccef012010-05-05 16:06:37 -0700297 // Don't setup D-Bus and GLib in test mode.
298 if (testing)
299 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700300
Darin Petkov703ec972010-04-27 11:02:18 -0700301 g_type_init();
Ben Chan6f598422013-06-22 06:29:36 -0700302 dbus_threads_init_default();
Darin Petkov65b01462010-04-14 13:32:20 -0700303
Darin Petkov703ec972010-04-27 11:02:18 -0700304 DBusError error;
305 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700306
David James3b3add52010-06-04 15:01:19 -0700307 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700308 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
309 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700310
Darin Petkov703ec972010-04-27 11:02:18 -0700311 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700312
Darin Petkov40f25732013-04-29 15:07:31 +0200313 vector<string> matches;
314 matches.push_back(
315 StringPrintf("type='signal',interface='%s',path='/',member='%s'",
316 kCrashReporterInterface,
317 kCrashReporterUserCrashSignal));
318 matches.push_back(
319 StringPrintf("type='signal',interface='%s',path='%s',member='%s'",
320 power_manager::kPowerManagerInterface,
321 power_manager::kPowerManagerServicePath,
322 power_manager::kPowerStateChangedSignal));
323 matches.push_back(
324 StringPrintf("type='signal',sender='%s',interface='%s',path='%s'",
325 login_manager::kSessionManagerServiceName,
326 login_manager::kSessionManagerInterface,
327 login_manager::kSessionManagerServicePath));
328
Darin Petkov703ec972010-04-27 11:02:18 -0700329 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov40f25732013-04-29 15:07:31 +0200330 for (vector<string>::const_iterator it = matches.begin();
331 it != matches.end(); ++it) {
332 const char* match = it->c_str();
Darin Petkov41e06232010-05-03 16:45:37 -0700333 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700334 dbus_bus_add_match(connection, match, &error);
335 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
336 "unable to add a match: " << SAFE_MESSAGE(error);
337 }
338
339 // Adds the D-Bus filter routine to be called back whenever one of
340 // the registered D-Bus matches is successful. The daemon is not
341 // activated for D-Bus messages that don't match.
342 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700343}
344
345void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700346 GMainLoop* loop = g_main_loop_new(NULL, false);
347 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700348}
349
Darin Petkov703ec972010-04-27 11:02:18 -0700350// static
351DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
352 DBusMessage* message,
353 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700354 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700355 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700356
357 int message_type = dbus_message_get_type(message);
358 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700359 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700360 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
361 }
362
363 // Signal messages always have interfaces.
364 const char* interface = dbus_message_get_interface(message);
365 CHECK(interface != NULL);
366
367 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
368
369 DBusMessageIter iter;
370 dbus_message_iter_init(message, &iter);
Darin Petkov40f25732013-04-29 15:07:31 +0200371 if (strcmp(interface, kCrashReporterInterface) == 0) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700372 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200373 kCrashReporterUserCrashSignal) == 0);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700374 daemon->ProcessUserCrash();
Darin Petkov40f25732013-04-29 15:07:31 +0200375 } else if (strcmp(interface, power_manager::kPowerManagerInterface) == 0) {
Darin Petkov41e06232010-05-03 16:45:37 -0700376 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200377 power_manager::kPowerStateChangedSignal) == 0);
David James3b3add52010-06-04 15:01:19 -0700378 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700379 dbus_message_iter_get_basic(&iter, &state_name);
Darin Petkov40f25732013-04-29 15:07:31 +0200380 daemon->PowerStateChanged(state_name, now);
381 } else if (strcmp(interface, login_manager::kSessionManagerInterface) == 0) {
382 const char* member = dbus_message_get_member(message);
383 if (strcmp(member, login_manager::kScreenIsLockedSignal) == 0) {
384 daemon->SetUserActiveState(false, now);
385 } else if (strcmp(member, login_manager::kScreenIsUnlockedSignal) == 0) {
386 daemon->SetUserActiveState(true, now);
387 } else if (strcmp(member, login_manager::kSessionStateChangedSignal) == 0) {
388 char* state_name;
389 dbus_message_iter_get_basic(&iter, &state_name);
390 daemon->SessionStateChanged(state_name, now);
391 }
Darin Petkov703ec972010-04-27 11:02:18 -0700392 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700393 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700394 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
395 }
396
397 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700398}
399
Darin Petkovf27f0362010-06-04 13:14:19 -0700400void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700401 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700402 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700403
404 if (power_state_ != kPowerStateOn)
405 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700406}
407
408MetricsDaemon::PowerState
409MetricsDaemon::LookupPowerState(const char* state_name) {
410 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700411 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700412 return static_cast<PowerState>(i);
413 }
414 }
Darin Petkov41e06232010-05-03 16:45:37 -0700415 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700416 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700417}
418
Darin Petkovf27f0362010-06-04 13:14:19 -0700419void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700420 DLOG(INFO) << "user session state: " << state_name;
421 session_state_ = LookupSessionState(state_name);
422 SetUserActiveState(session_state_ == kSessionStateStarted, now);
423}
424
425MetricsDaemon::SessionState
426MetricsDaemon::LookupSessionState(const char* state_name) {
427 for (int i = 0; i < kNumberSessionStates; i++) {
428 if (strcmp(state_name, kSessionStates_[i]) == 0) {
429 return static_cast<SessionState>(i);
430 }
431 }
432 DLOG(WARNING) << "unknown user session state: " << state_name;
433 return kUnknownSessionState;
434}
435
Darin Petkovf27f0362010-06-04 13:14:19 -0700436void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700437 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
438
439 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700440 // the day since Epoch, and logs the usage data. Guards against the
441 // time jumping back and forth due to the user changing it by
442 // discarding the new use time.
443 int seconds = 0;
444 if (user_active_ && now > user_active_last_) {
445 TimeDelta since_active = now - user_active_last_;
446 if (since_active < TimeDelta::FromSeconds(
447 kUseMonitorIntervalMax + kSecondsPerMinute)) {
448 seconds = static_cast<int>(since_active.InSeconds());
449 }
450 }
451 TimeDelta since_epoch = now - Time();
452 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700453 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700454 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700455 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700456
Ken Mixter4c5daa42010-08-26 18:35:06 -0700457 // Flush finished cycles of all frequency counters.
458 for (FrequencyCounters::iterator i = frequency_counters_.begin();
459 i != frequency_counters_.end(); ++i) {
460 i->second->FlushFinishedCycles();
461 }
462
Darin Petkov41e06232010-05-03 16:45:37 -0700463 // Schedules a use monitor on inactive->active transitions and
464 // unschedules it on active->inactive transitions.
465 if (!user_active_ && active)
466 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
467 else if (user_active_ && !active)
468 UnscheduleUseMonitor();
469
470 // Remembers the current active state and the time of the last
471 // activity update.
472 user_active_ = active;
473 user_active_last_ = now;
474}
475
Darin Petkov1bb904e2010-06-16 15:58:06 -0700476void MetricsDaemon::ProcessUserCrash() {
477 // Counts the active use time up to now.
478 SetUserActiveState(user_active_, Time::Now());
479
480 // Reports the active use time since the last crash and resets it.
481 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700482
Ken Mixter4c5daa42010-08-26 18:35:06 -0700483 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
484 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
485 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
486 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700487}
488
Darin Petkov38d5cb02010-06-24 12:10:26 -0700489void MetricsDaemon::ProcessKernelCrash() {
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 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700495
Ken Mixter4c5daa42010-08-26 18:35:06 -0700496 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
497 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
498 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
499 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700500}
501
Ken Mixterccd84c02010-08-16 19:57:13 -0700502void MetricsDaemon::ProcessUncleanShutdown() {
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 unclean_shutdown_interval_->Flush();
508
Ken Mixter4c5daa42010-08-26 18:35:06 -0700509 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
510 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
511 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
512 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700513}
514
Luigi Semenzato8accd332011-05-17 16:37:18 -0700515bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700516 FilePath crash_detected(crash_file);
517 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700518 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700519
520 // Deletes the crash-detected file so that the daemon doesn't report
521 // another kernel crash in case it's restarted.
522 file_util::Delete(crash_detected,
523 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700524 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700525}
526
Darin Petkov41e06232010-05-03 16:45:37 -0700527// static
528gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
529 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
530}
531
532bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700533 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700534
535 // If a new monitor source/instance is scheduled, returns false to
536 // tell GLib to destroy this monitor source/instance. Returns true
537 // otherwise to keep calling back this monitor.
538 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
539}
540
541bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
542{
Darin Petkov2ccef012010-05-05 16:06:37 -0700543 if (testing_)
544 return false;
545
Darin Petkov41e06232010-05-03 16:45:37 -0700546 // Caps the interval -- the bigger the interval, the more active use
547 // time will be potentially dropped on system shutdown.
548 if (interval > kUseMonitorIntervalMax)
549 interval = kUseMonitorIntervalMax;
550
551 if (backoff) {
552 // Back-off mode is used by the use monitor to reschedule itself
553 // with exponential back-off in time. This mode doesn't create a
554 // new timeout source if the new interval is the same as the old
555 // one. Also, if a new timeout source is created, the old one is
556 // not destroyed explicitly here -- it will be destroyed by GLib
557 // when the monitor returns FALSE (see UseMonitor and
558 // UseMonitorStatic).
559 if (interval == usemon_interval_)
560 return false;
561 } else {
562 UnscheduleUseMonitor();
563 }
564
565 // Schedules a new use monitor for |interval| seconds from now.
566 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
567 usemon_source_ = g_timeout_source_new_seconds(interval);
568 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
569 NULL); // No destroy notification.
570 g_source_attach(usemon_source_,
571 NULL); // Default context.
572 usemon_interval_ = interval;
573 return true;
574}
575
576void MetricsDaemon::UnscheduleUseMonitor() {
577 // If there's a use monitor scheduled already, destroys it.
578 if (usemon_source_ == NULL)
579 return;
580
581 DLOG(INFO) << "destroying use monitor";
582 g_source_destroy(usemon_source_);
583 usemon_source_ = NULL;
584 usemon_interval_ = 0;
585}
586
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700587void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800588 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700589 VmStatsReadStats(&page_faults_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800590 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700591 stats_state_ = kStatsLong;
592 stats_initial_time_ = GetActiveTime();
593 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700594 LOG(WARNING) << "not collecting disk stats";
595 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700596 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700597 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800598}
599
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700600void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800601 if (testing_) {
602 return;
603 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700604 g_timeout_add_seconds(wait, StatsCallbackStatic, this);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800605}
606
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700607bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800608 long int* write_sectors) {
609 int nchars;
610 int nitems;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700611 bool success = false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800612 char line[200];
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700613 if (diskstats_path_.empty()) {
614 return false;
615 }
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800616 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800617 if (file < 0) {
618 PLOG(WARNING) << "cannot open " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700619 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800620 }
621 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
622 if (nchars < 0) {
623 PLOG(WARNING) << "cannot read from " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700624 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800625 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700626 LOG_IF(WARNING, nchars == sizeof(line))
627 << "line too long in " << diskstats_path_;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800628 line[nchars] = '\0';
629 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
630 read_sectors, write_sectors);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700631 if (nitems == 2) {
632 success = true;
633 } else {
634 LOG(WARNING) << "found " << nitems << " items in "
635 << diskstats_path_ << ", expected 2";
636 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800637 }
638 HANDLE_EINTR(close(file));
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700639 return success;
640}
641
642bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
643 static const char kPageFaultSearchString[] = "\npgmajfault ";
644 bool success = false;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700645 // Each line in the file has the form
646 // <ID> <VALUE>
647 // for instance:
648 // nr_free_pages 213427
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700649 char* s = strstr(stats, kPageFaultSearchString);
650 if (s == NULL) {
651 LOG(WARNING) << "cannot find page fault entry in vmstats";
652 } else {
653 char* endp;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700654 // Skip <ID> and space. Don't count the terminating null.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700655 s += sizeof(kPageFaultSearchString) - 1;
656 *page_faults = strtol(s, &endp, 10);
657 if (*endp == '\n') {
658 success = true;
659 } else {
660 LOG(WARNING) << "error parsing vmstats";
661 }
662 }
663 return success;
664}
665
666bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
667 char buffer[4000];
668 int nchars;
669 int success = false;
670 if (testing_) {
671 return false;
672 }
673 int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
674 if (file < 0) {
675 PLOG(WARNING) << "cannot open " << vmstats_path_;
676 return false;
677 }
678 nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
679 LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
680 << "file too large in " << vmstats_path_;
681 if (nchars < 0) {
682 PLOG(WARNING) << "cannot read from " << vmstats_path_;
683 } else if (nchars == 0) {
684 LOG(WARNING) << vmstats_path_ << " is empty";
685 } else {
686 buffer[nchars] = '\0';
687 success = VmStatsParseStats(buffer, page_faults);
688 }
689 HANDLE_EINTR(close(file));
690 return success;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800691}
692
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700693bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
Luigi Semenzatod92d18c2013-06-04 13:24:21 -0700694 const FilePath sysfs_path(sysfs_file_name);
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700695 string value_string;
696 if (!file_util::ReadFileToString(sysfs_path, &value_string)) {
697 LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
698 return false;
699 }
700 if (!RemoveChars(value_string, "\n", &value_string)) {
701 LOG(WARNING) << "no newline in " << value_string;
702 // Continue even though the lack of newline is suspicious.
703 }
704 if (!base::StringToInt(value_string, value)) {
705 LOG(WARNING) << "cannot convert " << value_string << " to int";
706 return false;
707 }
708 return true;
709}
710
711void MetricsDaemon::SendCpuThrottleMetrics() {
712 // |max_freq| is 0 only the first time through.
713 static int max_freq = 0;
714 if (max_freq == -1)
715 // Give up, as sysfs did not report max_freq correctly.
716 return;
717 if (max_freq == 0 || testing_) {
718 // One-time initialization of max_freq. (Every time when testing.)
719 if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
720 max_freq = -1;
721 return;
722 }
723 if (max_freq == 0) {
724 LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
725 max_freq = -1;
726 return;
727 }
728 if (max_freq % 10000 == 1000) {
729 // Special case: system has turbo mode, and max non-turbo frequency is
730 // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
731 // being multiples of (at least) 10 MHz. Although there is no guarantee
732 // of this, it seems a fairly reasonable assumption. Otherwise we should
733 // read scaling_available_frequencies, sort the frequencies, compare the
734 // two highest ones, and check if they differ by 1000 (kHz) (and that's a
735 // hack too, no telling when it will change).
736 max_freq -= 1000;
737 }
738 }
739 int scaled_freq = 0;
740 if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
741 return;
742 // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
743 // scaled_freq is not the actual turbo frequency. We indicate this situation
744 // with a 101% value.
745 int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
746 SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102);
747}
748
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800749// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700750gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
751 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800752 return false; // one-time callback
753}
754
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700755// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700756
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700757void MetricsDaemon::StatsCallback() {
758 long int read_sectors_now, write_sectors_now, page_faults_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700759 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700760 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700761 if (testing_) {
762 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700763 delta_time = stats_state_ == kStatsShort ?
764 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700765 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700766 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
767 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700768 int delta_read = read_sectors_now - read_sectors_;
769 int delta_write = write_sectors_now - write_sectors_;
770 int read_sectors_per_second = delta_read / delta_time;
771 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700772 bool vmstats_success = VmStatsReadStats(&page_faults_now);
773 int delta_faults = page_faults_now - page_faults_;
774 int page_faults_per_second = delta_faults / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800775
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700776 switch (stats_state_) {
777 case kStatsShort:
778 if (diskstats_success) {
779 SendMetric(kMetricReadSectorsShortName,
780 read_sectors_per_second,
781 1,
782 kMetricSectorsIOMax,
783 kMetricSectorsBuckets);
784 SendMetric(kMetricWriteSectorsShortName,
785 write_sectors_per_second,
786 1,
787 kMetricSectorsIOMax,
788 kMetricSectorsBuckets);
789 }
790 if (vmstats_success) {
791 SendMetric(kMetricPageFaultsShortName,
792 page_faults_per_second,
793 1,
794 kMetricPageFaultsMax,
795 kMetricPageFaultsBuckets);
796 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800797 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700798 stats_state_ = kStatsLong;
799 ScheduleStatsCallback(kMetricStatsLongInterval -
800 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800801 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700802 case kStatsLong:
803 if (diskstats_success) {
804 SendMetric(kMetricReadSectorsLongName,
805 read_sectors_per_second,
806 1,
807 kMetricSectorsIOMax,
808 kMetricSectorsBuckets);
809 SendMetric(kMetricWriteSectorsLongName,
810 write_sectors_per_second,
811 1,
812 kMetricSectorsIOMax,
813 kMetricSectorsBuckets);
814 // Reset sector counters.
815 read_sectors_ = read_sectors_now;
816 write_sectors_ = write_sectors_now;
817 }
818 if (vmstats_success) {
819 SendMetric(kMetricPageFaultsLongName,
820 page_faults_per_second,
821 1,
822 kMetricPageFaultsMax,
823 kMetricPageFaultsBuckets);
824 page_faults_ = page_faults_now;
825 }
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700826 SendCpuThrottleMetrics();
Luigi Semenzato8accd332011-05-17 16:37:18 -0700827 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700828 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800829 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700830 stats_state_ = kStatsShort;
831 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800832 break;
833 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700834 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800835 }
836}
837
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700838void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
839 if (testing_) {
840 return;
841 }
842 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
843}
844
845// static
846gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
847 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
848}
849
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700850bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700851 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700852 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700853 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700854 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
855 return false;
856 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700857 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700858}
859
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700860bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700861 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700862 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
863 { "MemFree", "MemFree" },
864 { "Buffers", "Buffers" },
865 { "Cached", "Cached" },
866 // { "SwapCached", "SwapCached" },
867 { "Active", "Active" },
868 { "Inactive", "Inactive" },
869 { "ActiveAnon", "Active(anon)" },
870 { "InactiveAnon", "Inactive(anon)" },
871 { "ActiveFile" , "Active(file)" },
872 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800873 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700874 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800875 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
876 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700877 // { "Dirty", "Dirty" },
878 // { "Writeback", "Writeback" },
879 { "AnonPages", "AnonPages" },
880 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800881 { "Shmem", "Shmem", kMeminfoOp_HistLog },
882 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700883 // { "SReclaimable", "SReclaimable" },
884 // { "SUnreclaim", "SUnreclaim" },
885 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700886 vector<MeminfoRecord> fields(fields_array,
887 fields_array + arraysize(fields_array));
888 if (!FillMeminfo(meminfo_raw, &fields)) {
889 return false;
890 }
891 int total_memory = fields[0].value;
892 if (total_memory == 0) {
893 // this "cannot happen"
894 LOG(WARNING) << "borked meminfo parser";
895 return false;
896 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800897 int swap_total = 0;
898 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700899 // Send all fields retrieved, except total memory.
900 for (unsigned int i = 1; i < fields.size(); i++) {
901 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800902 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800903 switch (fields[i].op) {
904 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800905 // report value as percent of total memory
906 percent = fields[i].value * 100 / total_memory;
907 SendLinearMetric(metrics_name, percent, 100, 101);
908 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800909 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800910 // report value in kbytes, log scale, 4Gb max
911 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
912 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800913 case kMeminfoOp_SwapTotal:
914 swap_total = fields[i].value;
915 case kMeminfoOp_SwapFree:
916 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800917 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700918 }
919 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800920 if (swap_total > 0) {
921 int swap_used = swap_total - swap_free;
922 int swap_used_percent = swap_used * 100 / swap_total;
923 SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
924 SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
925 100, 101);
926 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700927 return true;
928}
929
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700930bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
931 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700932 vector<string> lines;
933 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700934
935 // Scan meminfo output and collect field values. Each field name has to
936 // match a meminfo entry (case insensitive) after removing non-alpha
937 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700938 unsigned int ifield = 0;
939 for (unsigned int iline = 0;
940 iline < nlines && ifield < fields->size();
941 iline++) {
942 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700943 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700944 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
945 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700946 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700947 (*fields)[ifield].value =
948 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700949 if (*rest != '\0') {
950 LOG(WARNING) << "missing meminfo value";
951 return false;
952 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700953 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700954 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700955 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700956 if (ifield < fields->size()) {
957 // End of input reached while scanning.
958 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
959 << " and following";
960 return false;
961 }
962 return true;
963}
964
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700965void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700966 double time_elapsed) {
967 if (testing_) {
968 return;
969 }
970 int interval = kMemuseIntervals[memuse_interval_index_];
971 int wait;
972 if (new_callback) {
973 memuse_initial_time_ = GetActiveTime();
974 wait = interval;
975 } else {
976 wait = ceil(interval - time_elapsed); // round up
977 }
978 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
979}
980
981// static
982gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
983 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
984 daemon->MemuseCallback();
985 return false;
986}
987
988void MetricsDaemon::MemuseCallback() {
989 // Since we only care about active time (i.e. uptime minus sleep time) but
990 // the callbacks are driven by real time (uptime), we check if we should
991 // reschedule this callback due to intervening sleep periods.
992 double now = GetActiveTime();
993 double active_time = now - memuse_initial_time_;
994 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
995 // Not enough active time has passed. Reschedule the callback.
996 ScheduleMemuseCallback(false, active_time);
997 } else {
998 // Enough active time has passed. Do the work, and (if we succeed) see if
999 // we need to do more.
1000 if (MemuseCallbackWork() &&
1001 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
1002 memuse_interval_index_++;
1003 ScheduleMemuseCallback(true, 0);
1004 }
1005 }
1006}
1007
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001008bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001009 string meminfo_raw;
1010 const FilePath meminfo_path("/proc/meminfo");
1011 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
1012 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
1013 return false;
1014 }
1015 return ProcessMemuse(meminfo_raw);
1016}
1017
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001018bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001019 static const MeminfoRecord fields_array[] = {
1020 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
1021 { "ActiveAnon", "Active(anon)" },
1022 { "InactiveAnon", "Inactive(anon)" },
1023 };
1024 vector<MeminfoRecord> fields(fields_array,
1025 fields_array + arraysize(fields_array));
1026 if (!FillMeminfo(meminfo_raw, &fields)) {
1027 return false;
1028 }
1029 int total = fields[0].value;
1030 int active_anon = fields[1].value;
1031 int inactive_anon = fields[2].value;
1032 if (total == 0) {
1033 // this "cannot happen"
1034 LOG(WARNING) << "borked meminfo parser";
1035 return false;
1036 }
1037 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
1038 memuse_interval_index_);
1039 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
1040 100, 101);
1041 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001042}
1043
Darin Petkovf1e85e42010-06-10 15:59:53 -07001044// static
Ken Mixterccd84c02010-08-16 19:57:13 -07001045void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -07001046 if (count <= 0)
1047 return;
1048
Darin Petkovf1e85e42010-06-10 15:59:53 -07001049 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
1050 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
1051 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
1052 kMetricDailyUseTimeMin,
1053 kMetricDailyUseTimeMax,
1054 kMetricDailyUseTimeBuckets);
1055}
1056
Darin Petkov38d5cb02010-06-24 12:10:26 -07001057void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -07001058 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -07001059 DLOG(INFO) << "received metric: " << name << " " << sample << " "
1060 << min << " " << max << " " << nbuckets;
1061 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -07001062}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001063
1064void MetricsDaemon::SendLinearMetric(const string& name, int sample,
1065 int max, int nbuckets) {
1066 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
1067 << max << " " << nbuckets;
1068 // TODO(semenzato): add a proper linear histogram to the Chrome external
1069 // metrics API.
1070 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1071 metrics_lib_->SendEnumToUMA(name, sample, max);
1072}