blob: 798d0228d2a9147197265f325ec6c8eb0fbb9d1d [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 Semenzato29c7ef92011-04-12 14:12:35 -070014#include <base/string_util.h>
Mike Frysinger71ebf982012-03-07 10:35:29 -050015#include <base/stringprintf.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070016#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070017
Darin Petkovf1e85e42010-06-10 15:59:53 -070018#include "counter.h"
19
Darin Petkovf27f0362010-06-04 13:14:19 -070020using base::Time;
21using base::TimeDelta;
22using base::TimeTicks;
Luigi Semenzato8accd332011-05-17 16:37:18 -070023using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070024using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070025using std::vector;
26
Darin Petkovf27f0362010-06-04 13:14:19 -070027
Darin Petkov703ec972010-04-27 11:02:18 -070028#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov1bb904e2010-06-16 15:58:06 -070029#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter"
David James6bf6e252010-06-06 18:52:40 -070030#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager"
Darin Petkov41e06232010-05-03 16:45:37 -070031#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface"
32
Darin Petkov41e06232010-05-03 16:45:37 -070033static const int kSecondsPerMinute = 60;
34static const int kMinutesPerHour = 60;
35static const int kHoursPerDay = 24;
36static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070037static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
38static const int kDaysPerWeek = 7;
39static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070040
41// The daily use monitor is scheduled to a 1-minute interval after
42// initial user activity and then it's exponentially backed off to
43// 10-minute intervals. Although not required, the back off is
44// implemented because the histogram buckets are spaced exponentially
45// anyway and to avoid too frequent metrics daemon process wake-ups
46// and file I/O.
47static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
48static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070049
Ken Mixterccd84c02010-08-16 19:57:13 -070050const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
51static const char kUncleanShutdownDetectedFile[] =
52 "/tmp/unclean-shutdown-detected";
53
Ken Mixter4c5daa42010-08-26 18:35:06 -070054// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070055const char MetricsDaemon::kMetricDailyUseTimeName[] =
56 "Logging.DailyUseTime";
57const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
58const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
59const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
60
Ken Mixterccd84c02010-08-16 19:57:13 -070061// crash interval metrics
62const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
63 "Logging.KernelCrashInterval";
64const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
65 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070066const char MetricsDaemon::kMetricUserCrashIntervalName[] =
67 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070068
69const int MetricsDaemon::kMetricCrashIntervalMin = 1;
70const int MetricsDaemon::kMetricCrashIntervalMax =
71 4 * kSecondsPerWeek;
72const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
73
74// crash frequency metrics
75const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
76 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070077const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
78 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070079const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
80 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070081const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
82 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070083const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
84 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070085const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
86 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070087const char MetricsDaemon::kMetricUserCrashesDailyName[] =
88 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070089const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
90 "Logging.UserCrashesWeekly";
91const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
92const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
93const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070094
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080095// disk stats metrics
96
97// The {Read,Write}Sectors numbers are in sectors/second.
98// A sector is usually 512 bytes.
99
100const char MetricsDaemon::kMetricReadSectorsLongName[] =
101 "Platform.ReadSectorsLong";
102const char MetricsDaemon::kMetricWriteSectorsLongName[] =
103 "Platform.WriteSectorsLong";
104const char MetricsDaemon::kMetricReadSectorsShortName[] =
105 "Platform.ReadSectorsShort";
106const char MetricsDaemon::kMetricWriteSectorsShortName[] =
107 "Platform.WriteSectorsShort";
108
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700109const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
110const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800111
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700112const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
113
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800114// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
115// sectors.
116const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
117const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700118// Page size is 4k, sector size is 0.5k. We're not interested in page fault
119// rates that the disk cannot sustain.
120const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
121const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
122
123// Major page faults, i.e. the ones that require data to be read from disk.
124
125const char MetricsDaemon::kMetricPageFaultsLongName[] =
126 "Platform.PageFaultsLong";
127const char MetricsDaemon::kMetricPageFaultsShortName[] =
128 "Platform.PageFaultsShort";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800129
Ken Mixter4c5daa42010-08-26 18:35:06 -0700130// persistent metrics path
131const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700132
Darin Petkov1bb904e2010-06-16 15:58:06 -0700133
Darin Petkov703ec972010-04-27 11:02:18 -0700134// static
Darin Petkov41e06232010-05-03 16:45:37 -0700135const char* MetricsDaemon::kDBusMatches_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700136 "type='signal',"
Darin Petkov1bb904e2010-06-16 15:58:06 -0700137 "interface='" DBUS_IFACE_CRASH_REPORTER "',"
138 "path='/',"
139 "member='UserCrash'",
140
141 "type='signal',"
Darin Petkov703ec972010-04-27 11:02:18 -0700142 "interface='" DBUS_IFACE_POWER_MANAGER "',"
Benson Leung53faeb02010-06-08 15:59:13 -0700143 "path='/'",
Darin Petkov41e06232010-05-03 16:45:37 -0700144
145 "type='signal',"
146 "sender='org.chromium.SessionManager',"
147 "interface='" DBUS_IFACE_SESSION_MANAGER "',"
148 "path='/org/chromium/SessionManager',"
149 "member='SessionStateChanged'",
Darin Petkov703ec972010-04-27 11:02:18 -0700150};
151
152// static
Darin Petkov41e06232010-05-03 16:45:37 -0700153const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700154#define STATE(name, capname) #name,
155#include "power_states.h"
156};
157
Darin Petkov41e06232010-05-03 16:45:37 -0700158// static
Darin Petkov41e06232010-05-03 16:45:37 -0700159const char* MetricsDaemon::kSessionStates_[] = {
160#define STATE(name, capname) #name,
161#include "session_states.h"
162};
163
Luigi Semenzato8accd332011-05-17 16:37:18 -0700164// Memory use stats collection intervals. We collect some memory use interval
165// at these intervals after boot, and we stop collecting after the last one,
166// with the assumption that in most cases the memory use won't change much
167// after that.
168static const int kMemuseIntervals[] = {
169 1 * kSecondsPerMinute, // 1 minute mark
170 4 * kSecondsPerMinute, // 5 minute mark
171 25 * kSecondsPerMinute, // 0.5 hour mark
172 120 * kSecondsPerMinute, // 2.5 hour mark
173 600 * kSecondsPerMinute, // 12.5 hour mark
174};
175
Darin Petkovf1e85e42010-06-10 15:59:53 -0700176MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700177 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700178 session_state_(kUnknownSessionState),
179 user_active_(false),
180 usemon_interval_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700181 usemon_source_(NULL),
182 memuse_initial_time_(0),
183 memuse_interval_index_(0),
184 read_sectors_(0),
185 write_sectors_(0),
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700186 page_faults_(0),
187 stats_state_(kStatsShort),
188 stats_initial_time_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700189
Ken Mixter4c5daa42010-08-26 18:35:06 -0700190MetricsDaemon::~MetricsDaemon() {
191 DeleteFrequencyCounters();
192}
193
Luigi Semenzato8accd332011-05-17 16:37:18 -0700194double MetricsDaemon::GetActiveTime() {
195 struct timespec ts;
196 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
197 if (r < 0) {
198 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
199 return 0;
200 } else {
201 return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000);
202 }
203}
204
Ken Mixter4c5daa42010-08-26 18:35:06 -0700205void MetricsDaemon::DeleteFrequencyCounters() {
206 for (FrequencyCounters::iterator i = frequency_counters_.begin();
207 i != frequency_counters_.end(); ++i) {
208 delete i->second;
209 i->second = NULL;
210 }
211}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700212
Darin Petkov2ccef012010-05-05 16:06:37 -0700213void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700214 if (run_as_daemon && daemon(0, 0) != 0)
215 return;
216
Ken Mixterccd84c02010-08-16 19:57:13 -0700217 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
218 ProcessKernelCrash();
219 }
220
221 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
222 ProcessUncleanShutdown();
223 }
224
Darin Petkov38d5cb02010-06-24 12:10:26 -0700225 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700226}
227
Ken Mixter4c5daa42010-08-26 18:35:06 -0700228FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
229 return FilePath(kMetricsPath).Append(histogram_name);
230}
231
232void MetricsDaemon::ConfigureCrashIntervalReporter(
233 const char* histogram_name,
234 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
235 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
236 FilePath file_path = GetHistogramPath(histogram_name);
237 (*reporter)->Init(file_path.value().c_str(),
238 histogram_name,
239 kMetricCrashIntervalMin,
240 kMetricCrashIntervalMax,
241 kMetricCrashIntervalBuckets);
242}
243
244void MetricsDaemon::ConfigureCrashFrequencyReporter(
245 const char* histogram_name) {
246 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
247 new chromeos_metrics::TaggedCounterReporter());
248 FilePath file_path = GetHistogramPath(histogram_name);
249 reporter->Init(file_path.value().c_str(),
250 histogram_name,
251 kMetricCrashFrequencyMin,
252 kMetricCrashFrequencyMax,
253 kMetricCrashFrequencyBuckets);
254 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
255 new chromeos_metrics::FrequencyCounter());
256 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
257 chromeos_metrics::kSecondsPerWeek :
258 chromeos_metrics::kSecondsPerDay;
259 new_counter->Init(
260 static_cast<chromeos_metrics::TaggedCounterInterface*>(
261 reporter.release()),
262 cycle_duration);
263 frequency_counters_[histogram_name] = new_counter.release();
264}
265
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800266void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700267 const string& diskstats_path,
268 const string& vmstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700269 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700270 DCHECK(metrics_lib != NULL);
271 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700272 chromeos_metrics::TaggedCounterReporter::
273 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700274
275 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700276 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700277 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700278
Ken Mixter4c5daa42010-08-26 18:35:06 -0700279 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
280 &kernel_crash_interval_);
281 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
282 &unclean_shutdown_interval_);
283 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
284 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700285
Ken Mixter4c5daa42010-08-26 18:35:06 -0700286 DeleteFrequencyCounters();
287 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700288 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
289 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
290 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
291 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
292 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
293 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
294 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700295
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700296 diskstats_path_ = diskstats_path;
297 vmstats_path_ = vmstats_path;
298 StatsReporterInit();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800299
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700300 // Start collecting meminfo stats.
301 ScheduleMeminfoCallback(kMetricMeminfoInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700302 ScheduleMemuseCallback(true, 0);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700303
Darin Petkov2ccef012010-05-05 16:06:37 -0700304 // Don't setup D-Bus and GLib in test mode.
305 if (testing)
306 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700307
Darin Petkov703ec972010-04-27 11:02:18 -0700308 g_thread_init(NULL);
309 g_type_init();
310 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700311
Darin Petkov703ec972010-04-27 11:02:18 -0700312 DBusError error;
313 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700314
David James3b3add52010-06-04 15:01:19 -0700315 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700316 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
317 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700318
Darin Petkov703ec972010-04-27 11:02:18 -0700319 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700320
Darin Petkov703ec972010-04-27 11:02:18 -0700321 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700322 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700323 const char* match = kDBusMatches_[m];
324 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700325 dbus_bus_add_match(connection, match, &error);
326 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
327 "unable to add a match: " << SAFE_MESSAGE(error);
328 }
329
330 // Adds the D-Bus filter routine to be called back whenever one of
331 // the registered D-Bus matches is successful. The daemon is not
332 // activated for D-Bus messages that don't match.
333 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700334}
335
336void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700337 GMainLoop* loop = g_main_loop_new(NULL, false);
338 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700339}
340
Darin Petkov703ec972010-04-27 11:02:18 -0700341// static
342DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
343 DBusMessage* message,
344 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700345 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700346 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700347
348 int message_type = dbus_message_get_type(message);
349 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700350 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700351 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
352 }
353
354 // Signal messages always have interfaces.
355 const char* interface = dbus_message_get_interface(message);
356 CHECK(interface != NULL);
357
358 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
359
360 DBusMessageIter iter;
361 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700362 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
363 CHECK(strcmp(dbus_message_get_member(message),
364 "UserCrash") == 0);
365 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700366 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700367 const char* member = dbus_message_get_member(message);
368 if (strcmp(member, "ScreenIsLocked") == 0) {
369 daemon->SetUserActiveState(false, now);
370 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
371 daemon->SetUserActiveState(true, now);
372 } else if (strcmp(member, "PowerStateChanged") == 0) {
373 char* state_name;
374 dbus_message_iter_get_basic(&iter, &state_name);
375 daemon->PowerStateChanged(state_name, now);
376 }
Darin Petkov41e06232010-05-03 16:45:37 -0700377 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
378 CHECK(strcmp(dbus_message_get_member(message),
379 "SessionStateChanged") == 0);
380
David James3b3add52010-06-04 15:01:19 -0700381 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700382 dbus_message_iter_get_basic(&iter, &state_name);
383 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700384 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700385 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700386 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
387 }
388
389 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700390}
391
Darin Petkovf27f0362010-06-04 13:14:19 -0700392void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700393 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700394 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700395
396 if (power_state_ != kPowerStateOn)
397 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700398}
399
400MetricsDaemon::PowerState
401MetricsDaemon::LookupPowerState(const char* state_name) {
402 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700403 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700404 return static_cast<PowerState>(i);
405 }
406 }
Darin Petkov41e06232010-05-03 16:45:37 -0700407 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700408 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700409}
410
Darin Petkovf27f0362010-06-04 13:14:19 -0700411void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700412 DLOG(INFO) << "user session state: " << state_name;
413 session_state_ = LookupSessionState(state_name);
414 SetUserActiveState(session_state_ == kSessionStateStarted, now);
415}
416
417MetricsDaemon::SessionState
418MetricsDaemon::LookupSessionState(const char* state_name) {
419 for (int i = 0; i < kNumberSessionStates; i++) {
420 if (strcmp(state_name, kSessionStates_[i]) == 0) {
421 return static_cast<SessionState>(i);
422 }
423 }
424 DLOG(WARNING) << "unknown user session state: " << state_name;
425 return kUnknownSessionState;
426}
427
Darin Petkovf27f0362010-06-04 13:14:19 -0700428void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700429 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
430
431 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700432 // the day since Epoch, and logs the usage data. Guards against the
433 // time jumping back and forth due to the user changing it by
434 // discarding the new use time.
435 int seconds = 0;
436 if (user_active_ && now > user_active_last_) {
437 TimeDelta since_active = now - user_active_last_;
438 if (since_active < TimeDelta::FromSeconds(
439 kUseMonitorIntervalMax + kSecondsPerMinute)) {
440 seconds = static_cast<int>(since_active.InSeconds());
441 }
442 }
443 TimeDelta since_epoch = now - Time();
444 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700445 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700446 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700447 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700448
Ken Mixter4c5daa42010-08-26 18:35:06 -0700449 // Flush finished cycles of all frequency counters.
450 for (FrequencyCounters::iterator i = frequency_counters_.begin();
451 i != frequency_counters_.end(); ++i) {
452 i->second->FlushFinishedCycles();
453 }
454
Darin Petkov41e06232010-05-03 16:45:37 -0700455 // Schedules a use monitor on inactive->active transitions and
456 // unschedules it on active->inactive transitions.
457 if (!user_active_ && active)
458 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
459 else if (user_active_ && !active)
460 UnscheduleUseMonitor();
461
462 // Remembers the current active state and the time of the last
463 // activity update.
464 user_active_ = active;
465 user_active_last_ = now;
466}
467
Darin Petkov1bb904e2010-06-16 15:58:06 -0700468void MetricsDaemon::ProcessUserCrash() {
469 // Counts the active use time up to now.
470 SetUserActiveState(user_active_, Time::Now());
471
472 // Reports the active use time since the last crash and resets it.
473 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700474
Ken Mixter4c5daa42010-08-26 18:35:06 -0700475 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
476 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
477 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
478 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700479}
480
Darin Petkov38d5cb02010-06-24 12:10:26 -0700481void MetricsDaemon::ProcessKernelCrash() {
482 // Counts the active use time up to now.
483 SetUserActiveState(user_active_, Time::Now());
484
485 // Reports the active use time since the last crash and resets it.
486 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700487
Ken Mixter4c5daa42010-08-26 18:35:06 -0700488 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
489 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
490 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
491 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700492}
493
Ken Mixterccd84c02010-08-16 19:57:13 -0700494void MetricsDaemon::ProcessUncleanShutdown() {
495 // Counts the active use time up to now.
496 SetUserActiveState(user_active_, Time::Now());
497
498 // Reports the active use time since the last crash and resets it.
499 unclean_shutdown_interval_->Flush();
500
Ken Mixter4c5daa42010-08-26 18:35:06 -0700501 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
502 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
503 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
504 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700505}
506
Luigi Semenzato8accd332011-05-17 16:37:18 -0700507bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700508 FilePath crash_detected(crash_file);
509 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700510 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700511
512 // Deletes the crash-detected file so that the daemon doesn't report
513 // another kernel crash in case it's restarted.
514 file_util::Delete(crash_detected,
515 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700516 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700517}
518
Darin Petkov41e06232010-05-03 16:45:37 -0700519// static
520gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
521 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
522}
523
524bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700525 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700526
527 // If a new monitor source/instance is scheduled, returns false to
528 // tell GLib to destroy this monitor source/instance. Returns true
529 // otherwise to keep calling back this monitor.
530 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
531}
532
533bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
534{
Darin Petkov2ccef012010-05-05 16:06:37 -0700535 if (testing_)
536 return false;
537
Darin Petkov41e06232010-05-03 16:45:37 -0700538 // Caps the interval -- the bigger the interval, the more active use
539 // time will be potentially dropped on system shutdown.
540 if (interval > kUseMonitorIntervalMax)
541 interval = kUseMonitorIntervalMax;
542
543 if (backoff) {
544 // Back-off mode is used by the use monitor to reschedule itself
545 // with exponential back-off in time. This mode doesn't create a
546 // new timeout source if the new interval is the same as the old
547 // one. Also, if a new timeout source is created, the old one is
548 // not destroyed explicitly here -- it will be destroyed by GLib
549 // when the monitor returns FALSE (see UseMonitor and
550 // UseMonitorStatic).
551 if (interval == usemon_interval_)
552 return false;
553 } else {
554 UnscheduleUseMonitor();
555 }
556
557 // Schedules a new use monitor for |interval| seconds from now.
558 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
559 usemon_source_ = g_timeout_source_new_seconds(interval);
560 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
561 NULL); // No destroy notification.
562 g_source_attach(usemon_source_,
563 NULL); // Default context.
564 usemon_interval_ = interval;
565 return true;
566}
567
568void MetricsDaemon::UnscheduleUseMonitor() {
569 // If there's a use monitor scheduled already, destroys it.
570 if (usemon_source_ == NULL)
571 return;
572
573 DLOG(INFO) << "destroying use monitor";
574 g_source_destroy(usemon_source_);
575 usemon_source_ = NULL;
576 usemon_interval_ = 0;
577}
578
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700579void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800580 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700581 VmStatsReadStats(&page_faults_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800582 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700583 stats_state_ = kStatsLong;
584 stats_initial_time_ = GetActiveTime();
585 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700586 LOG(WARNING) << "not collecting disk stats";
587 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700588 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700589 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800590}
591
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700592void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800593 if (testing_) {
594 return;
595 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700596 g_timeout_add_seconds(wait, StatsCallbackStatic, this);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800597}
598
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700599bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800600 long int* write_sectors) {
601 int nchars;
602 int nitems;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700603 bool success = false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800604 char line[200];
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700605 if (diskstats_path_.empty()) {
606 return false;
607 }
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800608 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800609 if (file < 0) {
610 PLOG(WARNING) << "cannot open " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700611 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800612 }
613 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
614 if (nchars < 0) {
615 PLOG(WARNING) << "cannot read from " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700616 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800617 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700618 LOG_IF(WARNING, nchars == sizeof(line))
619 << "line too long in " << diskstats_path_;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800620 line[nchars] = '\0';
621 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
622 read_sectors, write_sectors);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700623 if (nitems == 2) {
624 success = true;
625 } else {
626 LOG(WARNING) << "found " << nitems << " items in "
627 << diskstats_path_ << ", expected 2";
628 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800629 }
630 HANDLE_EINTR(close(file));
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700631 return success;
632}
633
634bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
635 static const char kPageFaultSearchString[] = "\npgmajfault ";
636 bool success = false;
637 /* Each line in the file has the form
638 * <ID> <VALUE>
639 * for instance:
640 * nr_free_pages 213427
641 */
642 char* s = strstr(stats, kPageFaultSearchString);
643 if (s == NULL) {
644 LOG(WARNING) << "cannot find page fault entry in vmstats";
645 } else {
646 char* endp;
647 /* Skip <ID> and space. Don't count the terminating null. */
648 s += sizeof(kPageFaultSearchString) - 1;
649 *page_faults = strtol(s, &endp, 10);
650 if (*endp == '\n') {
651 success = true;
652 } else {
653 LOG(WARNING) << "error parsing vmstats";
654 }
655 }
656 return success;
657}
658
659bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
660 char buffer[4000];
661 int nchars;
662 int success = false;
663 if (testing_) {
664 return false;
665 }
666 int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
667 if (file < 0) {
668 PLOG(WARNING) << "cannot open " << vmstats_path_;
669 return false;
670 }
671 nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
672 LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
673 << "file too large in " << vmstats_path_;
674 if (nchars < 0) {
675 PLOG(WARNING) << "cannot read from " << vmstats_path_;
676 } else if (nchars == 0) {
677 LOG(WARNING) << vmstats_path_ << " is empty";
678 } else {
679 buffer[nchars] = '\0';
680 success = VmStatsParseStats(buffer, page_faults);
681 }
682 HANDLE_EINTR(close(file));
683 return success;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800684}
685
686// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700687gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
688 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800689 return false; // one-time callback
690}
691
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700692// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700693
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700694void MetricsDaemon::StatsCallback() {
695 long int read_sectors_now, write_sectors_now, page_faults_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700696 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700697 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700698 if (testing_) {
699 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700700 delta_time = stats_state_ == kStatsShort ?
701 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700702 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700703 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
704 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700705 int delta_read = read_sectors_now - read_sectors_;
706 int delta_write = write_sectors_now - write_sectors_;
707 int read_sectors_per_second = delta_read / delta_time;
708 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700709 bool vmstats_success = VmStatsReadStats(&page_faults_now);
710 int delta_faults = page_faults_now - page_faults_;
711 int page_faults_per_second = delta_faults / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800712
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700713 switch (stats_state_) {
714 case kStatsShort:
715 if (diskstats_success) {
716 SendMetric(kMetricReadSectorsShortName,
717 read_sectors_per_second,
718 1,
719 kMetricSectorsIOMax,
720 kMetricSectorsBuckets);
721 SendMetric(kMetricWriteSectorsShortName,
722 write_sectors_per_second,
723 1,
724 kMetricSectorsIOMax,
725 kMetricSectorsBuckets);
726 }
727 if (vmstats_success) {
728 SendMetric(kMetricPageFaultsShortName,
729 page_faults_per_second,
730 1,
731 kMetricPageFaultsMax,
732 kMetricPageFaultsBuckets);
733 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800734 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700735 stats_state_ = kStatsLong;
736 ScheduleStatsCallback(kMetricStatsLongInterval -
737 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800738 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700739 case kStatsLong:
740 if (diskstats_success) {
741 SendMetric(kMetricReadSectorsLongName,
742 read_sectors_per_second,
743 1,
744 kMetricSectorsIOMax,
745 kMetricSectorsBuckets);
746 SendMetric(kMetricWriteSectorsLongName,
747 write_sectors_per_second,
748 1,
749 kMetricSectorsIOMax,
750 kMetricSectorsBuckets);
751 // Reset sector counters.
752 read_sectors_ = read_sectors_now;
753 write_sectors_ = write_sectors_now;
754 }
755 if (vmstats_success) {
756 SendMetric(kMetricPageFaultsLongName,
757 page_faults_per_second,
758 1,
759 kMetricPageFaultsMax,
760 kMetricPageFaultsBuckets);
761 page_faults_ = page_faults_now;
762 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700763 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700764 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800765 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700766 stats_state_ = kStatsShort;
767 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800768 break;
769 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700770 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800771 }
772}
773
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700774void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
775 if (testing_) {
776 return;
777 }
778 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
779}
780
781// static
782gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
783 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
784}
785
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700786bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700787 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700788 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700789 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700790 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
791 return false;
792 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700793 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700794}
795
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700796bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700797 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700798 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
799 { "MemFree", "MemFree" },
800 { "Buffers", "Buffers" },
801 { "Cached", "Cached" },
802 // { "SwapCached", "SwapCached" },
803 { "Active", "Active" },
804 { "Inactive", "Inactive" },
805 { "ActiveAnon", "Active(anon)" },
806 { "InactiveAnon", "Inactive(anon)" },
807 { "ActiveFile" , "Active(file)" },
808 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800809 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700810 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800811 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
812 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700813 // { "Dirty", "Dirty" },
814 // { "Writeback", "Writeback" },
815 { "AnonPages", "AnonPages" },
816 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800817 { "Shmem", "Shmem", kMeminfoOp_HistLog },
818 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700819 // { "SReclaimable", "SReclaimable" },
820 // { "SUnreclaim", "SUnreclaim" },
821 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700822 vector<MeminfoRecord> fields(fields_array,
823 fields_array + arraysize(fields_array));
824 if (!FillMeminfo(meminfo_raw, &fields)) {
825 return false;
826 }
827 int total_memory = fields[0].value;
828 if (total_memory == 0) {
829 // this "cannot happen"
830 LOG(WARNING) << "borked meminfo parser";
831 return false;
832 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800833 int swap_total = 0;
834 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700835 // Send all fields retrieved, except total memory.
836 for (unsigned int i = 1; i < fields.size(); i++) {
837 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800838 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800839 switch (fields[i].op) {
840 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800841 // report value as percent of total memory
842 percent = fields[i].value * 100 / total_memory;
843 SendLinearMetric(metrics_name, percent, 100, 101);
844 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800845 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800846 // report value in kbytes, log scale, 4Gb max
847 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
848 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800849 case kMeminfoOp_SwapTotal:
850 swap_total = fields[i].value;
851 case kMeminfoOp_SwapFree:
852 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800853 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700854 }
855 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800856 if (swap_total > 0) {
857 int swap_used = swap_total - swap_free;
858 int swap_used_percent = swap_used * 100 / swap_total;
859 SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
860 SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
861 100, 101);
862 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700863 return true;
864}
865
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700866bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
867 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700868 vector<string> lines;
869 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700870
871 // Scan meminfo output and collect field values. Each field name has to
872 // match a meminfo entry (case insensitive) after removing non-alpha
873 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700874 unsigned int ifield = 0;
875 for (unsigned int iline = 0;
876 iline < nlines && ifield < fields->size();
877 iline++) {
878 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700879 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700880 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
881 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700882 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700883 (*fields)[ifield].value =
884 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700885 if (*rest != '\0') {
886 LOG(WARNING) << "missing meminfo value";
887 return false;
888 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700889 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700890 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700891 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700892 if (ifield < fields->size()) {
893 // End of input reached while scanning.
894 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
895 << " and following";
896 return false;
897 }
898 return true;
899}
900
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700901void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700902 double time_elapsed) {
903 if (testing_) {
904 return;
905 }
906 int interval = kMemuseIntervals[memuse_interval_index_];
907 int wait;
908 if (new_callback) {
909 memuse_initial_time_ = GetActiveTime();
910 wait = interval;
911 } else {
912 wait = ceil(interval - time_elapsed); // round up
913 }
914 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
915}
916
917// static
918gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
919 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
920 daemon->MemuseCallback();
921 return false;
922}
923
924void MetricsDaemon::MemuseCallback() {
925 // Since we only care about active time (i.e. uptime minus sleep time) but
926 // the callbacks are driven by real time (uptime), we check if we should
927 // reschedule this callback due to intervening sleep periods.
928 double now = GetActiveTime();
929 double active_time = now - memuse_initial_time_;
930 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
931 // Not enough active time has passed. Reschedule the callback.
932 ScheduleMemuseCallback(false, active_time);
933 } else {
934 // Enough active time has passed. Do the work, and (if we succeed) see if
935 // we need to do more.
936 if (MemuseCallbackWork() &&
937 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
938 memuse_interval_index_++;
939 ScheduleMemuseCallback(true, 0);
940 }
941 }
942}
943
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700944bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700945 string meminfo_raw;
946 const FilePath meminfo_path("/proc/meminfo");
947 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
948 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
949 return false;
950 }
951 return ProcessMemuse(meminfo_raw);
952}
953
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700954bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700955 static const MeminfoRecord fields_array[] = {
956 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
957 { "ActiveAnon", "Active(anon)" },
958 { "InactiveAnon", "Inactive(anon)" },
959 };
960 vector<MeminfoRecord> fields(fields_array,
961 fields_array + arraysize(fields_array));
962 if (!FillMeminfo(meminfo_raw, &fields)) {
963 return false;
964 }
965 int total = fields[0].value;
966 int active_anon = fields[1].value;
967 int inactive_anon = fields[2].value;
968 if (total == 0) {
969 // this "cannot happen"
970 LOG(WARNING) << "borked meminfo parser";
971 return false;
972 }
973 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
974 memuse_interval_index_);
975 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
976 100, 101);
977 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700978}
979
Darin Petkovf1e85e42010-06-10 15:59:53 -0700980// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700981void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700982 if (count <= 0)
983 return;
984
Darin Petkovf1e85e42010-06-10 15:59:53 -0700985 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
986 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
987 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
988 kMetricDailyUseTimeMin,
989 kMetricDailyUseTimeMax,
990 kMetricDailyUseTimeBuckets);
991}
992
Darin Petkov38d5cb02010-06-24 12:10:26 -0700993void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700994 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700995 DLOG(INFO) << "received metric: " << name << " " << sample << " "
996 << min << " " << max << " " << nbuckets;
997 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700998}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700999
1000void MetricsDaemon::SendLinearMetric(const string& name, int sample,
1001 int max, int nbuckets) {
1002 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
1003 << max << " " << nbuckets;
1004 // TODO(semenzato): add a proper linear histogram to the Chrome external
1005 // metrics API.
1006 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1007 metrics_lib_->SendEnumToUMA(name, sample, max);
1008}