blob: 3b9e9dba55d349b6f3f3f0e21c17bb15a081c7cc [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) {
695 const string sysfs_dirpath(testing_ ?
696 "./" : "/sys/devices/system/cpu/cpu0/cpufreq/");
697 const FilePath sysfs_path(sysfs_dirpath + sysfs_file_name);
698 string value_string;
699 if (!file_util::ReadFileToString(sysfs_path, &value_string)) {
700 LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
701 return false;
702 }
703 if (!RemoveChars(value_string, "\n", &value_string)) {
704 LOG(WARNING) << "no newline in " << value_string;
705 // Continue even though the lack of newline is suspicious.
706 }
707 if (!base::StringToInt(value_string, value)) {
708 LOG(WARNING) << "cannot convert " << value_string << " to int";
709 return false;
710 }
711 return true;
712}
713
714void MetricsDaemon::SendCpuThrottleMetrics() {
715 // |max_freq| is 0 only the first time through.
716 static int max_freq = 0;
717 if (max_freq == -1)
718 // Give up, as sysfs did not report max_freq correctly.
719 return;
720 if (max_freq == 0 || testing_) {
721 // One-time initialization of max_freq. (Every time when testing.)
722 if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
723 max_freq = -1;
724 return;
725 }
726 if (max_freq == 0) {
727 LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
728 max_freq = -1;
729 return;
730 }
731 if (max_freq % 10000 == 1000) {
732 // Special case: system has turbo mode, and max non-turbo frequency is
733 // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
734 // being multiples of (at least) 10 MHz. Although there is no guarantee
735 // of this, it seems a fairly reasonable assumption. Otherwise we should
736 // read scaling_available_frequencies, sort the frequencies, compare the
737 // two highest ones, and check if they differ by 1000 (kHz) (and that's a
738 // hack too, no telling when it will change).
739 max_freq -= 1000;
740 }
741 }
742 int scaled_freq = 0;
743 if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
744 return;
745 // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
746 // scaled_freq is not the actual turbo frequency. We indicate this situation
747 // with a 101% value.
748 int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
749 SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102);
750}
751
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800752// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700753gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
754 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800755 return false; // one-time callback
756}
757
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700758// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700759
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700760void MetricsDaemon::StatsCallback() {
761 long int read_sectors_now, write_sectors_now, page_faults_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700762 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700763 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700764 if (testing_) {
765 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700766 delta_time = stats_state_ == kStatsShort ?
767 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700768 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700769 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
770 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700771 int delta_read = read_sectors_now - read_sectors_;
772 int delta_write = write_sectors_now - write_sectors_;
773 int read_sectors_per_second = delta_read / delta_time;
774 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700775 bool vmstats_success = VmStatsReadStats(&page_faults_now);
776 int delta_faults = page_faults_now - page_faults_;
777 int page_faults_per_second = delta_faults / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800778
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700779 switch (stats_state_) {
780 case kStatsShort:
781 if (diskstats_success) {
782 SendMetric(kMetricReadSectorsShortName,
783 read_sectors_per_second,
784 1,
785 kMetricSectorsIOMax,
786 kMetricSectorsBuckets);
787 SendMetric(kMetricWriteSectorsShortName,
788 write_sectors_per_second,
789 1,
790 kMetricSectorsIOMax,
791 kMetricSectorsBuckets);
792 }
793 if (vmstats_success) {
794 SendMetric(kMetricPageFaultsShortName,
795 page_faults_per_second,
796 1,
797 kMetricPageFaultsMax,
798 kMetricPageFaultsBuckets);
799 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800800 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700801 stats_state_ = kStatsLong;
802 ScheduleStatsCallback(kMetricStatsLongInterval -
803 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800804 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700805 case kStatsLong:
806 if (diskstats_success) {
807 SendMetric(kMetricReadSectorsLongName,
808 read_sectors_per_second,
809 1,
810 kMetricSectorsIOMax,
811 kMetricSectorsBuckets);
812 SendMetric(kMetricWriteSectorsLongName,
813 write_sectors_per_second,
814 1,
815 kMetricSectorsIOMax,
816 kMetricSectorsBuckets);
817 // Reset sector counters.
818 read_sectors_ = read_sectors_now;
819 write_sectors_ = write_sectors_now;
820 }
821 if (vmstats_success) {
822 SendMetric(kMetricPageFaultsLongName,
823 page_faults_per_second,
824 1,
825 kMetricPageFaultsMax,
826 kMetricPageFaultsBuckets);
827 page_faults_ = page_faults_now;
828 }
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700829 SendCpuThrottleMetrics();
Luigi Semenzato8accd332011-05-17 16:37:18 -0700830 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700831 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800832 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700833 stats_state_ = kStatsShort;
834 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800835 break;
836 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700837 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800838 }
839}
840
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700841void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
842 if (testing_) {
843 return;
844 }
845 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
846}
847
848// static
849gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
850 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
851}
852
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700853bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700854 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700855 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700856 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700857 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
858 return false;
859 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700860 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700861}
862
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700863bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700864 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700865 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
866 { "MemFree", "MemFree" },
867 { "Buffers", "Buffers" },
868 { "Cached", "Cached" },
869 // { "SwapCached", "SwapCached" },
870 { "Active", "Active" },
871 { "Inactive", "Inactive" },
872 { "ActiveAnon", "Active(anon)" },
873 { "InactiveAnon", "Inactive(anon)" },
874 { "ActiveFile" , "Active(file)" },
875 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800876 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700877 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800878 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
879 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700880 // { "Dirty", "Dirty" },
881 // { "Writeback", "Writeback" },
882 { "AnonPages", "AnonPages" },
883 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800884 { "Shmem", "Shmem", kMeminfoOp_HistLog },
885 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700886 // { "SReclaimable", "SReclaimable" },
887 // { "SUnreclaim", "SUnreclaim" },
888 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700889 vector<MeminfoRecord> fields(fields_array,
890 fields_array + arraysize(fields_array));
891 if (!FillMeminfo(meminfo_raw, &fields)) {
892 return false;
893 }
894 int total_memory = fields[0].value;
895 if (total_memory == 0) {
896 // this "cannot happen"
897 LOG(WARNING) << "borked meminfo parser";
898 return false;
899 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800900 int swap_total = 0;
901 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700902 // Send all fields retrieved, except total memory.
903 for (unsigned int i = 1; i < fields.size(); i++) {
904 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800905 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800906 switch (fields[i].op) {
907 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800908 // report value as percent of total memory
909 percent = fields[i].value * 100 / total_memory;
910 SendLinearMetric(metrics_name, percent, 100, 101);
911 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800912 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800913 // report value in kbytes, log scale, 4Gb max
914 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
915 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800916 case kMeminfoOp_SwapTotal:
917 swap_total = fields[i].value;
918 case kMeminfoOp_SwapFree:
919 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800920 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700921 }
922 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800923 if (swap_total > 0) {
924 int swap_used = swap_total - swap_free;
925 int swap_used_percent = swap_used * 100 / swap_total;
926 SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
927 SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
928 100, 101);
929 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700930 return true;
931}
932
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700933bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
934 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700935 vector<string> lines;
936 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700937
938 // Scan meminfo output and collect field values. Each field name has to
939 // match a meminfo entry (case insensitive) after removing non-alpha
940 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700941 unsigned int ifield = 0;
942 for (unsigned int iline = 0;
943 iline < nlines && ifield < fields->size();
944 iline++) {
945 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700946 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700947 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
948 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700949 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700950 (*fields)[ifield].value =
951 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700952 if (*rest != '\0') {
953 LOG(WARNING) << "missing meminfo value";
954 return false;
955 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700956 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700957 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700958 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700959 if (ifield < fields->size()) {
960 // End of input reached while scanning.
961 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
962 << " and following";
963 return false;
964 }
965 return true;
966}
967
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700968void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700969 double time_elapsed) {
970 if (testing_) {
971 return;
972 }
973 int interval = kMemuseIntervals[memuse_interval_index_];
974 int wait;
975 if (new_callback) {
976 memuse_initial_time_ = GetActiveTime();
977 wait = interval;
978 } else {
979 wait = ceil(interval - time_elapsed); // round up
980 }
981 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
982}
983
984// static
985gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
986 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
987 daemon->MemuseCallback();
988 return false;
989}
990
991void MetricsDaemon::MemuseCallback() {
992 // Since we only care about active time (i.e. uptime minus sleep time) but
993 // the callbacks are driven by real time (uptime), we check if we should
994 // reschedule this callback due to intervening sleep periods.
995 double now = GetActiveTime();
996 double active_time = now - memuse_initial_time_;
997 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
998 // Not enough active time has passed. Reschedule the callback.
999 ScheduleMemuseCallback(false, active_time);
1000 } else {
1001 // Enough active time has passed. Do the work, and (if we succeed) see if
1002 // we need to do more.
1003 if (MemuseCallbackWork() &&
1004 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
1005 memuse_interval_index_++;
1006 ScheduleMemuseCallback(true, 0);
1007 }
1008 }
1009}
1010
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001011bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001012 string meminfo_raw;
1013 const FilePath meminfo_path("/proc/meminfo");
1014 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
1015 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
1016 return false;
1017 }
1018 return ProcessMemuse(meminfo_raw);
1019}
1020
Luigi Semenzato5bd764f2011-10-14 12:03:35 -07001021bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -07001022 static const MeminfoRecord fields_array[] = {
1023 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
1024 { "ActiveAnon", "Active(anon)" },
1025 { "InactiveAnon", "Inactive(anon)" },
1026 };
1027 vector<MeminfoRecord> fields(fields_array,
1028 fields_array + arraysize(fields_array));
1029 if (!FillMeminfo(meminfo_raw, &fields)) {
1030 return false;
1031 }
1032 int total = fields[0].value;
1033 int active_anon = fields[1].value;
1034 int inactive_anon = fields[2].value;
1035 if (total == 0) {
1036 // this "cannot happen"
1037 LOG(WARNING) << "borked meminfo parser";
1038 return false;
1039 }
1040 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
1041 memuse_interval_index_);
1042 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
1043 100, 101);
1044 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001045}
1046
Darin Petkovf1e85e42010-06-10 15:59:53 -07001047// static
Ken Mixterccd84c02010-08-16 19:57:13 -07001048void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -07001049 if (count <= 0)
1050 return;
1051
Darin Petkovf1e85e42010-06-10 15:59:53 -07001052 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
1053 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
1054 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
1055 kMetricDailyUseTimeMin,
1056 kMetricDailyUseTimeMax,
1057 kMetricDailyUseTimeBuckets);
1058}
1059
Darin Petkov38d5cb02010-06-24 12:10:26 -07001060void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -07001061 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -07001062 DLOG(INFO) << "received metric: " << name << " " << sample << " "
1063 << min << " " << max << " " << nbuckets;
1064 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -07001065}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001066
1067void MetricsDaemon::SendLinearMetric(const string& name, int sample,
1068 int max, int nbuckets) {
1069 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
1070 << max << " " << nbuckets;
1071 // TODO(semenzato): add a proper linear histogram to the Chrome external
1072 // metrics API.
1073 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1074 metrics_lib_->SendEnumToUMA(name, sample, max);
1075}