blob: ad6bf945b49fb2dc01358954a0b048b58ca29f8d [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_thread_init(NULL);
302 g_type_init();
303 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700304
Darin Petkov703ec972010-04-27 11:02:18 -0700305 DBusError error;
306 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700307
David James3b3add52010-06-04 15:01:19 -0700308 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700309 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
310 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700311
Darin Petkov703ec972010-04-27 11:02:18 -0700312 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700313
Darin Petkov40f25732013-04-29 15:07:31 +0200314 vector<string> matches;
315 matches.push_back(
316 StringPrintf("type='signal',interface='%s',path='/',member='%s'",
317 kCrashReporterInterface,
318 kCrashReporterUserCrashSignal));
319 matches.push_back(
320 StringPrintf("type='signal',interface='%s',path='%s',member='%s'",
321 power_manager::kPowerManagerInterface,
322 power_manager::kPowerManagerServicePath,
323 power_manager::kPowerStateChangedSignal));
324 matches.push_back(
325 StringPrintf("type='signal',sender='%s',interface='%s',path='%s'",
326 login_manager::kSessionManagerServiceName,
327 login_manager::kSessionManagerInterface,
328 login_manager::kSessionManagerServicePath));
329
Darin Petkov703ec972010-04-27 11:02:18 -0700330 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov40f25732013-04-29 15:07:31 +0200331 for (vector<string>::const_iterator it = matches.begin();
332 it != matches.end(); ++it) {
333 const char* match = it->c_str();
Darin Petkov41e06232010-05-03 16:45:37 -0700334 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700335 dbus_bus_add_match(connection, match, &error);
336 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
337 "unable to add a match: " << SAFE_MESSAGE(error);
338 }
339
340 // Adds the D-Bus filter routine to be called back whenever one of
341 // the registered D-Bus matches is successful. The daemon is not
342 // activated for D-Bus messages that don't match.
343 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700344}
345
346void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700347 GMainLoop* loop = g_main_loop_new(NULL, false);
348 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700349}
350
Darin Petkov703ec972010-04-27 11:02:18 -0700351// static
352DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
353 DBusMessage* message,
354 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700355 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700356 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700357
358 int message_type = dbus_message_get_type(message);
359 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700360 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700361 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
362 }
363
364 // Signal messages always have interfaces.
365 const char* interface = dbus_message_get_interface(message);
366 CHECK(interface != NULL);
367
368 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
369
370 DBusMessageIter iter;
371 dbus_message_iter_init(message, &iter);
Darin Petkov40f25732013-04-29 15:07:31 +0200372 if (strcmp(interface, kCrashReporterInterface) == 0) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700373 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200374 kCrashReporterUserCrashSignal) == 0);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700375 daemon->ProcessUserCrash();
Darin Petkov40f25732013-04-29 15:07:31 +0200376 } else if (strcmp(interface, power_manager::kPowerManagerInterface) == 0) {
Darin Petkov41e06232010-05-03 16:45:37 -0700377 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200378 power_manager::kPowerStateChangedSignal) == 0);
David James3b3add52010-06-04 15:01:19 -0700379 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700380 dbus_message_iter_get_basic(&iter, &state_name);
Darin Petkov40f25732013-04-29 15:07:31 +0200381 daemon->PowerStateChanged(state_name, now);
382 } else if (strcmp(interface, login_manager::kSessionManagerInterface) == 0) {
383 const char* member = dbus_message_get_member(message);
384 if (strcmp(member, login_manager::kScreenIsLockedSignal) == 0) {
385 daemon->SetUserActiveState(false, now);
386 } else if (strcmp(member, login_manager::kScreenIsUnlockedSignal) == 0) {
387 daemon->SetUserActiveState(true, now);
388 } else if (strcmp(member, login_manager::kSessionStateChangedSignal) == 0) {
389 char* state_name;
390 dbus_message_iter_get_basic(&iter, &state_name);
391 daemon->SessionStateChanged(state_name, now);
392 }
Darin Petkov703ec972010-04-27 11:02:18 -0700393 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700394 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700395 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
396 }
397
398 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700399}
400
Darin Petkovf27f0362010-06-04 13:14:19 -0700401void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700402 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700403 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700404
405 if (power_state_ != kPowerStateOn)
406 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700407}
408
409MetricsDaemon::PowerState
410MetricsDaemon::LookupPowerState(const char* state_name) {
411 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700412 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700413 return static_cast<PowerState>(i);
414 }
415 }
Darin Petkov41e06232010-05-03 16:45:37 -0700416 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700417 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700418}
419
Darin Petkovf27f0362010-06-04 13:14:19 -0700420void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700421 DLOG(INFO) << "user session state: " << state_name;
422 session_state_ = LookupSessionState(state_name);
423 SetUserActiveState(session_state_ == kSessionStateStarted, now);
424}
425
426MetricsDaemon::SessionState
427MetricsDaemon::LookupSessionState(const char* state_name) {
428 for (int i = 0; i < kNumberSessionStates; i++) {
429 if (strcmp(state_name, kSessionStates_[i]) == 0) {
430 return static_cast<SessionState>(i);
431 }
432 }
433 DLOG(WARNING) << "unknown user session state: " << state_name;
434 return kUnknownSessionState;
435}
436
Darin Petkovf27f0362010-06-04 13:14:19 -0700437void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700438 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
439
440 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700441 // the day since Epoch, and logs the usage data. Guards against the
442 // time jumping back and forth due to the user changing it by
443 // discarding the new use time.
444 int seconds = 0;
445 if (user_active_ && now > user_active_last_) {
446 TimeDelta since_active = now - user_active_last_;
447 if (since_active < TimeDelta::FromSeconds(
448 kUseMonitorIntervalMax + kSecondsPerMinute)) {
449 seconds = static_cast<int>(since_active.InSeconds());
450 }
451 }
452 TimeDelta since_epoch = now - Time();
453 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700454 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700455 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700456 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700457
Ken Mixter4c5daa42010-08-26 18:35:06 -0700458 // Flush finished cycles of all frequency counters.
459 for (FrequencyCounters::iterator i = frequency_counters_.begin();
460 i != frequency_counters_.end(); ++i) {
461 i->second->FlushFinishedCycles();
462 }
463
Darin Petkov41e06232010-05-03 16:45:37 -0700464 // Schedules a use monitor on inactive->active transitions and
465 // unschedules it on active->inactive transitions.
466 if (!user_active_ && active)
467 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
468 else if (user_active_ && !active)
469 UnscheduleUseMonitor();
470
471 // Remembers the current active state and the time of the last
472 // activity update.
473 user_active_ = active;
474 user_active_last_ = now;
475}
476
Darin Petkov1bb904e2010-06-16 15:58:06 -0700477void MetricsDaemon::ProcessUserCrash() {
478 // Counts the active use time up to now.
479 SetUserActiveState(user_active_, Time::Now());
480
481 // Reports the active use time since the last crash and resets it.
482 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700483
Ken Mixter4c5daa42010-08-26 18:35:06 -0700484 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
485 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
486 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
487 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700488}
489
Darin Petkov38d5cb02010-06-24 12:10:26 -0700490void MetricsDaemon::ProcessKernelCrash() {
491 // Counts the active use time up to now.
492 SetUserActiveState(user_active_, Time::Now());
493
494 // Reports the active use time since the last crash and resets it.
495 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700496
Ken Mixter4c5daa42010-08-26 18:35:06 -0700497 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
498 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
499 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
500 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700501}
502
Ken Mixterccd84c02010-08-16 19:57:13 -0700503void MetricsDaemon::ProcessUncleanShutdown() {
504 // Counts the active use time up to now.
505 SetUserActiveState(user_active_, Time::Now());
506
507 // Reports the active use time since the last crash and resets it.
508 unclean_shutdown_interval_->Flush();
509
Ken Mixter4c5daa42010-08-26 18:35:06 -0700510 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
511 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
512 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
513 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700514}
515
Luigi Semenzato8accd332011-05-17 16:37:18 -0700516bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700517 FilePath crash_detected(crash_file);
518 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700519 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700520
521 // Deletes the crash-detected file so that the daemon doesn't report
522 // another kernel crash in case it's restarted.
523 file_util::Delete(crash_detected,
524 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700525 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700526}
527
Darin Petkov41e06232010-05-03 16:45:37 -0700528// static
529gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
530 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
531}
532
533bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700534 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700535
536 // If a new monitor source/instance is scheduled, returns false to
537 // tell GLib to destroy this monitor source/instance. Returns true
538 // otherwise to keep calling back this monitor.
539 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
540}
541
542bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
543{
Darin Petkov2ccef012010-05-05 16:06:37 -0700544 if (testing_)
545 return false;
546
Darin Petkov41e06232010-05-03 16:45:37 -0700547 // Caps the interval -- the bigger the interval, the more active use
548 // time will be potentially dropped on system shutdown.
549 if (interval > kUseMonitorIntervalMax)
550 interval = kUseMonitorIntervalMax;
551
552 if (backoff) {
553 // Back-off mode is used by the use monitor to reschedule itself
554 // with exponential back-off in time. This mode doesn't create a
555 // new timeout source if the new interval is the same as the old
556 // one. Also, if a new timeout source is created, the old one is
557 // not destroyed explicitly here -- it will be destroyed by GLib
558 // when the monitor returns FALSE (see UseMonitor and
559 // UseMonitorStatic).
560 if (interval == usemon_interval_)
561 return false;
562 } else {
563 UnscheduleUseMonitor();
564 }
565
566 // Schedules a new use monitor for |interval| seconds from now.
567 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
568 usemon_source_ = g_timeout_source_new_seconds(interval);
569 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
570 NULL); // No destroy notification.
571 g_source_attach(usemon_source_,
572 NULL); // Default context.
573 usemon_interval_ = interval;
574 return true;
575}
576
577void MetricsDaemon::UnscheduleUseMonitor() {
578 // If there's a use monitor scheduled already, destroys it.
579 if (usemon_source_ == NULL)
580 return;
581
582 DLOG(INFO) << "destroying use monitor";
583 g_source_destroy(usemon_source_);
584 usemon_source_ = NULL;
585 usemon_interval_ = 0;
586}
587
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700588void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800589 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700590 VmStatsReadStats(&page_faults_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800591 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700592 stats_state_ = kStatsLong;
593 stats_initial_time_ = GetActiveTime();
594 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700595 LOG(WARNING) << "not collecting disk stats";
596 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700597 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700598 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800599}
600
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700601void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800602 if (testing_) {
603 return;
604 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700605 g_timeout_add_seconds(wait, StatsCallbackStatic, this);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800606}
607
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700608bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800609 long int* write_sectors) {
610 int nchars;
611 int nitems;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700612 bool success = false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800613 char line[200];
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700614 if (diskstats_path_.empty()) {
615 return false;
616 }
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800617 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800618 if (file < 0) {
619 PLOG(WARNING) << "cannot open " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700620 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800621 }
622 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
623 if (nchars < 0) {
624 PLOG(WARNING) << "cannot read from " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700625 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800626 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700627 LOG_IF(WARNING, nchars == sizeof(line))
628 << "line too long in " << diskstats_path_;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800629 line[nchars] = '\0';
630 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
631 read_sectors, write_sectors);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700632 if (nitems == 2) {
633 success = true;
634 } else {
635 LOG(WARNING) << "found " << nitems << " items in "
636 << diskstats_path_ << ", expected 2";
637 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800638 }
639 HANDLE_EINTR(close(file));
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700640 return success;
641}
642
643bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
644 static const char kPageFaultSearchString[] = "\npgmajfault ";
645 bool success = false;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700646 // Each line in the file has the form
647 // <ID> <VALUE>
648 // for instance:
649 // nr_free_pages 213427
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700650 char* s = strstr(stats, kPageFaultSearchString);
651 if (s == NULL) {
652 LOG(WARNING) << "cannot find page fault entry in vmstats";
653 } else {
654 char* endp;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700655 // Skip <ID> and space. Don't count the terminating null.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700656 s += sizeof(kPageFaultSearchString) - 1;
657 *page_faults = strtol(s, &endp, 10);
658 if (*endp == '\n') {
659 success = true;
660 } else {
661 LOG(WARNING) << "error parsing vmstats";
662 }
663 }
664 return success;
665}
666
667bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
668 char buffer[4000];
669 int nchars;
670 int success = false;
671 if (testing_) {
672 return false;
673 }
674 int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
675 if (file < 0) {
676 PLOG(WARNING) << "cannot open " << vmstats_path_;
677 return false;
678 }
679 nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
680 LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
681 << "file too large in " << vmstats_path_;
682 if (nchars < 0) {
683 PLOG(WARNING) << "cannot read from " << vmstats_path_;
684 } else if (nchars == 0) {
685 LOG(WARNING) << vmstats_path_ << " is empty";
686 } else {
687 buffer[nchars] = '\0';
688 success = VmStatsParseStats(buffer, page_faults);
689 }
690 HANDLE_EINTR(close(file));
691 return success;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800692}
693
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700694bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
Luigi Semenzatod92d18c2013-06-04 13:24:21 -0700695 const FilePath sysfs_path(sysfs_file_name);
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700696 string value_string;
697 if (!file_util::ReadFileToString(sysfs_path, &value_string)) {
698 LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
699 return false;
700 }
701 if (!RemoveChars(value_string, "\n", &value_string)) {
702 LOG(WARNING) << "no newline in " << value_string;
703 // Continue even though the lack of newline is suspicious.
704 }
705 if (!base::StringToInt(value_string, value)) {
706 LOG(WARNING) << "cannot convert " << value_string << " to int";
707 return false;
708 }
709 return true;
710}
711
712void MetricsDaemon::SendCpuThrottleMetrics() {
713 // |max_freq| is 0 only the first time through.
714 static int max_freq = 0;
715 if (max_freq == -1)
716 // Give up, as sysfs did not report max_freq correctly.
717 return;
718 if (max_freq == 0 || testing_) {
719 // One-time initialization of max_freq. (Every time when testing.)
720 if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
721 max_freq = -1;
722 return;
723 }
724 if (max_freq == 0) {
725 LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
726 max_freq = -1;
727 return;
728 }
729 if (max_freq % 10000 == 1000) {
730 // Special case: system has turbo mode, and max non-turbo frequency is
731 // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
732 // being multiples of (at least) 10 MHz. Although there is no guarantee
733 // of this, it seems a fairly reasonable assumption. Otherwise we should
734 // read scaling_available_frequencies, sort the frequencies, compare the
735 // two highest ones, and check if they differ by 1000 (kHz) (and that's a
736 // hack too, no telling when it will change).
737 max_freq -= 1000;
738 }
739 }
740 int scaled_freq = 0;
741 if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
742 return;
743 // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
744 // scaled_freq is not the actual turbo frequency. We indicate this situation
745 // with a 101% value.
746 int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
747 SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102);
748}
749
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800750// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700751gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
752 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800753 return false; // one-time callback
754}
755
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700756// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700757
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700758void MetricsDaemon::StatsCallback() {
759 long int read_sectors_now, write_sectors_now, page_faults_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700760 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700761 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700762 if (testing_) {
763 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700764 delta_time = stats_state_ == kStatsShort ?
765 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700766 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700767 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
768 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700769 int delta_read = read_sectors_now - read_sectors_;
770 int delta_write = write_sectors_now - write_sectors_;
771 int read_sectors_per_second = delta_read / delta_time;
772 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700773 bool vmstats_success = VmStatsReadStats(&page_faults_now);
774 int delta_faults = page_faults_now - page_faults_;
775 int page_faults_per_second = delta_faults / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800776
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700777 switch (stats_state_) {
778 case kStatsShort:
779 if (diskstats_success) {
780 SendMetric(kMetricReadSectorsShortName,
781 read_sectors_per_second,
782 1,
783 kMetricSectorsIOMax,
784 kMetricSectorsBuckets);
785 SendMetric(kMetricWriteSectorsShortName,
786 write_sectors_per_second,
787 1,
788 kMetricSectorsIOMax,
789 kMetricSectorsBuckets);
790 }
791 if (vmstats_success) {
792 SendMetric(kMetricPageFaultsShortName,
793 page_faults_per_second,
794 1,
795 kMetricPageFaultsMax,
796 kMetricPageFaultsBuckets);
797 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800798 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700799 stats_state_ = kStatsLong;
800 ScheduleStatsCallback(kMetricStatsLongInterval -
801 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800802 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700803 case kStatsLong:
804 if (diskstats_success) {
805 SendMetric(kMetricReadSectorsLongName,
806 read_sectors_per_second,
807 1,
808 kMetricSectorsIOMax,
809 kMetricSectorsBuckets);
810 SendMetric(kMetricWriteSectorsLongName,
811 write_sectors_per_second,
812 1,
813 kMetricSectorsIOMax,
814 kMetricSectorsBuckets);
815 // Reset sector counters.
816 read_sectors_ = read_sectors_now;
817 write_sectors_ = write_sectors_now;
818 }
819 if (vmstats_success) {
820 SendMetric(kMetricPageFaultsLongName,
821 page_faults_per_second,
822 1,
823 kMetricPageFaultsMax,
824 kMetricPageFaultsBuckets);
825 page_faults_ = page_faults_now;
826 }
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700827 SendCpuThrottleMetrics();
Luigi Semenzato8accd332011-05-17 16:37:18 -0700828 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700829 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800830 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700831 stats_state_ = kStatsShort;
832 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800833 break;
834 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700835 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800836 }
837}
838
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700839void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
840 if (testing_) {
841 return;
842 }
843 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
844}
845
846// static
847gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
848 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
849}
850
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700851bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700852 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700853 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700854 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700855 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
856 return false;
857 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700858 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700859}
860
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700861bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700862 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700863 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
864 { "MemFree", "MemFree" },
865 { "Buffers", "Buffers" },
866 { "Cached", "Cached" },
867 // { "SwapCached", "SwapCached" },
868 { "Active", "Active" },
869 { "Inactive", "Inactive" },
870 { "ActiveAnon", "Active(anon)" },
871 { "InactiveAnon", "Inactive(anon)" },
872 { "ActiveFile" , "Active(file)" },
873 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800874 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700875 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800876 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
877 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700878 // { "Dirty", "Dirty" },
879 // { "Writeback", "Writeback" },
880 { "AnonPages", "AnonPages" },
881 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800882 { "Shmem", "Shmem", kMeminfoOp_HistLog },
883 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700884 // { "SReclaimable", "SReclaimable" },
885 // { "SUnreclaim", "SUnreclaim" },
886 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700887 vector<MeminfoRecord> fields(fields_array,
888 fields_array + arraysize(fields_array));
889 if (!FillMeminfo(meminfo_raw, &fields)) {
890 return false;
891 }
892 int total_memory = fields[0].value;
893 if (total_memory == 0) {
894 // this "cannot happen"
895 LOG(WARNING) << "borked meminfo parser";
896 return false;
897 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800898 int swap_total = 0;
899 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700900 // Send all fields retrieved, except total memory.
901 for (unsigned int i = 1; i < fields.size(); i++) {
902 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800903 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800904 switch (fields[i].op) {
905 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800906 // report value as percent of total memory
907 percent = fields[i].value * 100 / total_memory;
908 SendLinearMetric(metrics_name, percent, 100, 101);
909 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800910 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800911 // report value in kbytes, log scale, 4Gb max
912 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
913 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800914 case kMeminfoOp_SwapTotal:
915 swap_total = fields[i].value;
916 case kMeminfoOp_SwapFree:
917 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800918 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700919 }
920 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800921 if (swap_total > 0) {
922 int swap_used = swap_total - swap_free;
923 int swap_used_percent = swap_used * 100 / swap_total;
924 SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
925 SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
926 100, 101);
927 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700928 return true;
929}
930
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700931bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
932 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700933 vector<string> lines;
934 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700935
936 // Scan meminfo output and collect field values. Each field name has to
937 // match a meminfo entry (case insensitive) after removing non-alpha
938 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700939 unsigned int ifield = 0;
940 for (unsigned int iline = 0;
941 iline < nlines && ifield < fields->size();
942 iline++) {
943 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700944 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700945 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
946 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700947 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700948 (*fields)[ifield].value =
949 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700950 if (*rest != '\0') {
951 LOG(WARNING) << "missing meminfo value";
952 return false;
953 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700954 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700955 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700956 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700957 if (ifield < fields->size()) {
958 // End of input reached while scanning.
959 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
960 << " and following";
961 return false;
962 }
963 return true;
964}
965
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700966void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700967 double time_elapsed) {
968 if (testing_) {
969 return;
970 }
971 int interval = kMemuseIntervals[memuse_interval_index_];
972 int wait;
973 if (new_callback) {
974 memuse_initial_time_ = GetActiveTime();
975 wait = interval;
976 } else {
977 wait = ceil(interval - time_elapsed); // round up
978 }
979 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
980}
981
982// static
983gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
984 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
985 daemon->MemuseCallback();
986 return false;
987}
988
989void MetricsDaemon::MemuseCallback() {
990 // Since we only care about active time (i.e. uptime minus sleep time) but
991 // the callbacks are driven by real time (uptime), we check if we should
992 // reschedule this callback due to intervening sleep periods.
993 double now = GetActiveTime();
994 double active_time = now - memuse_initial_time_;
995 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
996 // Not enough active time has passed. Reschedule the callback.
997 ScheduleMemuseCallback(false, active_time);
998 } else {
999 // Enough active time has passed. Do the work, and (if we succeed) see if
1000 // we need to do more.
1001 if (MemuseCallbackWork() &&
1002 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
1003 memuse_interval_index_++;
1004 ScheduleMemuseCallback(true, 0);
1005 }
1006 }
1007}
1008
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001009bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001010 string meminfo_raw;
1011 const FilePath meminfo_path("/proc/meminfo");
1012 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
1013 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
1014 return false;
1015 }
1016 return ProcessMemuse(meminfo_raw);
1017}
1018
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001019bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001020 static const MeminfoRecord fields_array[] = {
1021 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
1022 { "ActiveAnon", "Active(anon)" },
1023 { "InactiveAnon", "Inactive(anon)" },
1024 };
1025 vector<MeminfoRecord> fields(fields_array,
1026 fields_array + arraysize(fields_array));
1027 if (!FillMeminfo(meminfo_raw, &fields)) {
1028 return false;
1029 }
1030 int total = fields[0].value;
1031 int active_anon = fields[1].value;
1032 int inactive_anon = fields[2].value;
1033 if (total == 0) {
1034 // this "cannot happen"
1035 LOG(WARNING) << "borked meminfo parser";
1036 return false;
1037 }
1038 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
1039 memuse_interval_index_);
1040 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
1041 100, 101);
1042 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001043}
1044
Darin Petkovf1e85e42010-06-10 15:59:53 -07001045// static
Ken Mixterccd84c02010-08-16 19:57:13 -07001046void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -07001047 if (count <= 0)
1048 return;
1049
Darin Petkovf1e85e42010-06-10 15:59:53 -07001050 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
1051 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
1052 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
1053 kMetricDailyUseTimeMin,
1054 kMetricDailyUseTimeMax,
1055 kMetricDailyUseTimeBuckets);
1056}
1057
Darin Petkov38d5cb02010-06-24 12:10:26 -07001058void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -07001059 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -07001060 DLOG(INFO) << "received metric: " << name << " " << sample << " "
1061 << min << " " << max << " " << nbuckets;
1062 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -07001063}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001064
1065void MetricsDaemon::SendLinearMetric(const string& name, int sample,
1066 int max, int nbuckets) {
1067 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
1068 << max << " " << nbuckets;
1069 // TODO(semenzato): add a proper linear histogram to the Chrome external
1070 // metrics API.
1071 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1072 metrics_lib_->SendEnumToUMA(name, sample, max);
1073}