blob: 1684f814f1efd2439110877422da7e76759c388b [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>
Ken Mixter4c5daa42010-08-26 18:35:06 -070015#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070016
Darin Petkovf1e85e42010-06-10 15:59:53 -070017#include "counter.h"
18
Darin Petkovf27f0362010-06-04 13:14:19 -070019using base::Time;
20using base::TimeDelta;
21using base::TimeTicks;
Luigi Semenzato8accd332011-05-17 16:37:18 -070022using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070023using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070024using std::vector;
25
Darin Petkovf27f0362010-06-04 13:14:19 -070026
Darin Petkov703ec972010-04-27 11:02:18 -070027#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov1bb904e2010-06-16 15:58:06 -070028#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter"
David James6bf6e252010-06-06 18:52:40 -070029#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager"
Darin Petkov41e06232010-05-03 16:45:37 -070030#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface"
31
Darin Petkov41e06232010-05-03 16:45:37 -070032static const int kSecondsPerMinute = 60;
33static const int kMinutesPerHour = 60;
34static const int kHoursPerDay = 24;
35static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070036static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
37static const int kDaysPerWeek = 7;
38static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070039
40// The daily use monitor is scheduled to a 1-minute interval after
41// initial user activity and then it's exponentially backed off to
42// 10-minute intervals. Although not required, the back off is
43// implemented because the histogram buckets are spaced exponentially
44// anyway and to avoid too frequent metrics daemon process wake-ups
45// and file I/O.
46static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
47static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070048
Ken Mixterccd84c02010-08-16 19:57:13 -070049const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
50static const char kUncleanShutdownDetectedFile[] =
51 "/tmp/unclean-shutdown-detected";
52
Ken Mixter4c5daa42010-08-26 18:35:06 -070053// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070054const char MetricsDaemon::kMetricDailyUseTimeName[] =
55 "Logging.DailyUseTime";
56const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
57const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
58const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
59
Ken Mixterccd84c02010-08-16 19:57:13 -070060// crash interval metrics
61const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
62 "Logging.KernelCrashInterval";
63const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
64 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070065const char MetricsDaemon::kMetricUserCrashIntervalName[] =
66 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070067
68const int MetricsDaemon::kMetricCrashIntervalMin = 1;
69const int MetricsDaemon::kMetricCrashIntervalMax =
70 4 * kSecondsPerWeek;
71const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
72
73// crash frequency metrics
74const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
75 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070076const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
77 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070078const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
79 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070080const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
81 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070082const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
83 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070084const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
85 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070086const char MetricsDaemon::kMetricUserCrashesDailyName[] =
87 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070088const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
89 "Logging.UserCrashesWeekly";
90const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
91const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
92const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070093
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080094// disk stats metrics
95
96// The {Read,Write}Sectors numbers are in sectors/second.
97// A sector is usually 512 bytes.
98
99const char MetricsDaemon::kMetricReadSectorsLongName[] =
100 "Platform.ReadSectorsLong";
101const char MetricsDaemon::kMetricWriteSectorsLongName[] =
102 "Platform.WriteSectorsLong";
103const char MetricsDaemon::kMetricReadSectorsShortName[] =
104 "Platform.ReadSectorsShort";
105const char MetricsDaemon::kMetricWriteSectorsShortName[] =
106 "Platform.WriteSectorsShort";
107
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700108const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
109const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800110
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700111const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
112
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800113// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
114// sectors.
115const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
116const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700117// Page size is 4k, sector size is 0.5k. We're not interested in page fault
118// rates that the disk cannot sustain.
119const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
120const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
121
122// Major page faults, i.e. the ones that require data to be read from disk.
123
124const char MetricsDaemon::kMetricPageFaultsLongName[] =
125 "Platform.PageFaultsLong";
126const char MetricsDaemon::kMetricPageFaultsShortName[] =
127 "Platform.PageFaultsShort";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800128
Ken Mixter4c5daa42010-08-26 18:35:06 -0700129// persistent metrics path
130const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700131
Darin Petkov1bb904e2010-06-16 15:58:06 -0700132
Darin Petkov703ec972010-04-27 11:02:18 -0700133// static
Darin Petkov41e06232010-05-03 16:45:37 -0700134const char* MetricsDaemon::kDBusMatches_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700135 "type='signal',"
Darin Petkov1bb904e2010-06-16 15:58:06 -0700136 "interface='" DBUS_IFACE_CRASH_REPORTER "',"
137 "path='/',"
138 "member='UserCrash'",
139
140 "type='signal',"
Darin Petkov703ec972010-04-27 11:02:18 -0700141 "interface='" DBUS_IFACE_POWER_MANAGER "',"
Benson Leung53faeb02010-06-08 15:59:13 -0700142 "path='/'",
Darin Petkov41e06232010-05-03 16:45:37 -0700143
144 "type='signal',"
145 "sender='org.chromium.SessionManager',"
146 "interface='" DBUS_IFACE_SESSION_MANAGER "',"
147 "path='/org/chromium/SessionManager',"
148 "member='SessionStateChanged'",
Darin Petkov703ec972010-04-27 11:02:18 -0700149};
150
151// static
Darin Petkov41e06232010-05-03 16:45:37 -0700152const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700153#define STATE(name, capname) #name,
154#include "power_states.h"
155};
156
Darin Petkov41e06232010-05-03 16:45:37 -0700157// static
Darin Petkov41e06232010-05-03 16:45:37 -0700158const char* MetricsDaemon::kSessionStates_[] = {
159#define STATE(name, capname) #name,
160#include "session_states.h"
161};
162
Luigi Semenzato8accd332011-05-17 16:37:18 -0700163// Memory use stats collection intervals. We collect some memory use interval
164// at these intervals after boot, and we stop collecting after the last one,
165// with the assumption that in most cases the memory use won't change much
166// after that.
167static const int kMemuseIntervals[] = {
168 1 * kSecondsPerMinute, // 1 minute mark
169 4 * kSecondsPerMinute, // 5 minute mark
170 25 * kSecondsPerMinute, // 0.5 hour mark
171 120 * kSecondsPerMinute, // 2.5 hour mark
172 600 * kSecondsPerMinute, // 12.5 hour mark
173};
174
Darin Petkovf1e85e42010-06-10 15:59:53 -0700175MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700176 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700177 session_state_(kUnknownSessionState),
178 user_active_(false),
179 usemon_interval_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700180 usemon_source_(NULL),
181 memuse_initial_time_(0),
182 memuse_interval_index_(0),
183 read_sectors_(0),
184 write_sectors_(0),
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700185 page_faults_(0),
186 stats_state_(kStatsShort),
187 stats_initial_time_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700188
Ken Mixter4c5daa42010-08-26 18:35:06 -0700189MetricsDaemon::~MetricsDaemon() {
190 DeleteFrequencyCounters();
191}
192
Luigi Semenzato8accd332011-05-17 16:37:18 -0700193double MetricsDaemon::GetActiveTime() {
194 struct timespec ts;
195 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
196 if (r < 0) {
197 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
198 return 0;
199 } else {
200 return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000);
201 }
202}
203
Ken Mixter4c5daa42010-08-26 18:35:06 -0700204void MetricsDaemon::DeleteFrequencyCounters() {
205 for (FrequencyCounters::iterator i = frequency_counters_.begin();
206 i != frequency_counters_.end(); ++i) {
207 delete i->second;
208 i->second = NULL;
209 }
210}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700211
Darin Petkov2ccef012010-05-05 16:06:37 -0700212void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700213 if (run_as_daemon && daemon(0, 0) != 0)
214 return;
215
Ken Mixterccd84c02010-08-16 19:57:13 -0700216 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
217 ProcessKernelCrash();
218 }
219
220 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
221 ProcessUncleanShutdown();
222 }
223
Darin Petkov38d5cb02010-06-24 12:10:26 -0700224 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700225}
226
Ken Mixter4c5daa42010-08-26 18:35:06 -0700227FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
228 return FilePath(kMetricsPath).Append(histogram_name);
229}
230
231void MetricsDaemon::ConfigureCrashIntervalReporter(
232 const char* histogram_name,
233 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
234 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
235 FilePath file_path = GetHistogramPath(histogram_name);
236 (*reporter)->Init(file_path.value().c_str(),
237 histogram_name,
238 kMetricCrashIntervalMin,
239 kMetricCrashIntervalMax,
240 kMetricCrashIntervalBuckets);
241}
242
243void MetricsDaemon::ConfigureCrashFrequencyReporter(
244 const char* histogram_name) {
245 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
246 new chromeos_metrics::TaggedCounterReporter());
247 FilePath file_path = GetHistogramPath(histogram_name);
248 reporter->Init(file_path.value().c_str(),
249 histogram_name,
250 kMetricCrashFrequencyMin,
251 kMetricCrashFrequencyMax,
252 kMetricCrashFrequencyBuckets);
253 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
254 new chromeos_metrics::FrequencyCounter());
255 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
256 chromeos_metrics::kSecondsPerWeek :
257 chromeos_metrics::kSecondsPerDay;
258 new_counter->Init(
259 static_cast<chromeos_metrics::TaggedCounterInterface*>(
260 reporter.release()),
261 cycle_duration);
262 frequency_counters_[histogram_name] = new_counter.release();
263}
264
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800265void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700266 const string& diskstats_path,
267 const string& vmstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700268 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700269 DCHECK(metrics_lib != NULL);
270 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700271 chromeos_metrics::TaggedCounterReporter::
272 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700273
274 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700275 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700276 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700277
Ken Mixter4c5daa42010-08-26 18:35:06 -0700278 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
279 &kernel_crash_interval_);
280 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
281 &unclean_shutdown_interval_);
282 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
283 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700284
Ken Mixter4c5daa42010-08-26 18:35:06 -0700285 DeleteFrequencyCounters();
286 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700287 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
288 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
289 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
290 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
291 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
292 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
293 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700294
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700295 diskstats_path_ = diskstats_path;
296 vmstats_path_ = vmstats_path;
297 StatsReporterInit();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800298
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700299 // Start collecting meminfo stats.
300 ScheduleMeminfoCallback(kMetricMeminfoInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700301 ScheduleMemuseCallback(true, 0);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700302
Darin Petkov2ccef012010-05-05 16:06:37 -0700303 // Don't setup D-Bus and GLib in test mode.
304 if (testing)
305 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700306
Darin Petkov703ec972010-04-27 11:02:18 -0700307 g_thread_init(NULL);
308 g_type_init();
309 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700310
Darin Petkov703ec972010-04-27 11:02:18 -0700311 DBusError error;
312 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700313
David James3b3add52010-06-04 15:01:19 -0700314 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700315 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
316 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700317
Darin Petkov703ec972010-04-27 11:02:18 -0700318 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700319
Darin Petkov703ec972010-04-27 11:02:18 -0700320 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700321 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700322 const char* match = kDBusMatches_[m];
323 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700324 dbus_bus_add_match(connection, match, &error);
325 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
326 "unable to add a match: " << SAFE_MESSAGE(error);
327 }
328
329 // Adds the D-Bus filter routine to be called back whenever one of
330 // the registered D-Bus matches is successful. The daemon is not
331 // activated for D-Bus messages that don't match.
332 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700333}
334
335void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700336 GMainLoop* loop = g_main_loop_new(NULL, false);
337 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700338}
339
Darin Petkov703ec972010-04-27 11:02:18 -0700340// static
341DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
342 DBusMessage* message,
343 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700344 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700345 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700346
347 int message_type = dbus_message_get_type(message);
348 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700349 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700350 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
351 }
352
353 // Signal messages always have interfaces.
354 const char* interface = dbus_message_get_interface(message);
355 CHECK(interface != NULL);
356
357 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
358
359 DBusMessageIter iter;
360 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700361 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
362 CHECK(strcmp(dbus_message_get_member(message),
363 "UserCrash") == 0);
364 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700365 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700366 const char* member = dbus_message_get_member(message);
367 if (strcmp(member, "ScreenIsLocked") == 0) {
368 daemon->SetUserActiveState(false, now);
369 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
370 daemon->SetUserActiveState(true, now);
371 } else if (strcmp(member, "PowerStateChanged") == 0) {
372 char* state_name;
373 dbus_message_iter_get_basic(&iter, &state_name);
374 daemon->PowerStateChanged(state_name, now);
375 }
Darin Petkov41e06232010-05-03 16:45:37 -0700376 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
377 CHECK(strcmp(dbus_message_get_member(message),
378 "SessionStateChanged") == 0);
379
David James3b3add52010-06-04 15:01:19 -0700380 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700381 dbus_message_iter_get_basic(&iter, &state_name);
382 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700383 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700384 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700385 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
386 }
387
388 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700389}
390
Darin Petkovf27f0362010-06-04 13:14:19 -0700391void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700392 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700393 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700394
395 if (power_state_ != kPowerStateOn)
396 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700397}
398
399MetricsDaemon::PowerState
400MetricsDaemon::LookupPowerState(const char* state_name) {
401 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700402 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700403 return static_cast<PowerState>(i);
404 }
405 }
Darin Petkov41e06232010-05-03 16:45:37 -0700406 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700407 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700408}
409
Darin Petkovf27f0362010-06-04 13:14:19 -0700410void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700411 DLOG(INFO) << "user session state: " << state_name;
412 session_state_ = LookupSessionState(state_name);
413 SetUserActiveState(session_state_ == kSessionStateStarted, now);
414}
415
416MetricsDaemon::SessionState
417MetricsDaemon::LookupSessionState(const char* state_name) {
418 for (int i = 0; i < kNumberSessionStates; i++) {
419 if (strcmp(state_name, kSessionStates_[i]) == 0) {
420 return static_cast<SessionState>(i);
421 }
422 }
423 DLOG(WARNING) << "unknown user session state: " << state_name;
424 return kUnknownSessionState;
425}
426
Darin Petkovf27f0362010-06-04 13:14:19 -0700427void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700428 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
429
430 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700431 // the day since Epoch, and logs the usage data. Guards against the
432 // time jumping back and forth due to the user changing it by
433 // discarding the new use time.
434 int seconds = 0;
435 if (user_active_ && now > user_active_last_) {
436 TimeDelta since_active = now - user_active_last_;
437 if (since_active < TimeDelta::FromSeconds(
438 kUseMonitorIntervalMax + kSecondsPerMinute)) {
439 seconds = static_cast<int>(since_active.InSeconds());
440 }
441 }
442 TimeDelta since_epoch = now - Time();
443 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700444 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700445 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700446 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700447
Ken Mixter4c5daa42010-08-26 18:35:06 -0700448 // Flush finished cycles of all frequency counters.
449 for (FrequencyCounters::iterator i = frequency_counters_.begin();
450 i != frequency_counters_.end(); ++i) {
451 i->second->FlushFinishedCycles();
452 }
453
Darin Petkov41e06232010-05-03 16:45:37 -0700454 // Schedules a use monitor on inactive->active transitions and
455 // unschedules it on active->inactive transitions.
456 if (!user_active_ && active)
457 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
458 else if (user_active_ && !active)
459 UnscheduleUseMonitor();
460
461 // Remembers the current active state and the time of the last
462 // activity update.
463 user_active_ = active;
464 user_active_last_ = now;
465}
466
Darin Petkov1bb904e2010-06-16 15:58:06 -0700467void MetricsDaemon::ProcessUserCrash() {
468 // Counts the active use time up to now.
469 SetUserActiveState(user_active_, Time::Now());
470
471 // Reports the active use time since the last crash and resets it.
472 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700473
Ken Mixter4c5daa42010-08-26 18:35:06 -0700474 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
475 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
476 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
477 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700478}
479
Darin Petkov38d5cb02010-06-24 12:10:26 -0700480void MetricsDaemon::ProcessKernelCrash() {
481 // Counts the active use time up to now.
482 SetUserActiveState(user_active_, Time::Now());
483
484 // Reports the active use time since the last crash and resets it.
485 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700486
Ken Mixter4c5daa42010-08-26 18:35:06 -0700487 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
488 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
489 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
490 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700491}
492
Ken Mixterccd84c02010-08-16 19:57:13 -0700493void MetricsDaemon::ProcessUncleanShutdown() {
494 // Counts the active use time up to now.
495 SetUserActiveState(user_active_, Time::Now());
496
497 // Reports the active use time since the last crash and resets it.
498 unclean_shutdown_interval_->Flush();
499
Ken Mixter4c5daa42010-08-26 18:35:06 -0700500 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
501 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
502 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
503 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700504}
505
Luigi Semenzato8accd332011-05-17 16:37:18 -0700506bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700507 FilePath crash_detected(crash_file);
508 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700509 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700510
511 // Deletes the crash-detected file so that the daemon doesn't report
512 // another kernel crash in case it's restarted.
513 file_util::Delete(crash_detected,
514 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700515 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700516}
517
Darin Petkov41e06232010-05-03 16:45:37 -0700518// static
519gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
520 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
521}
522
523bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700524 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700525
526 // If a new monitor source/instance is scheduled, returns false to
527 // tell GLib to destroy this monitor source/instance. Returns true
528 // otherwise to keep calling back this monitor.
529 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
530}
531
532bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
533{
Darin Petkov2ccef012010-05-05 16:06:37 -0700534 if (testing_)
535 return false;
536
Darin Petkov41e06232010-05-03 16:45:37 -0700537 // Caps the interval -- the bigger the interval, the more active use
538 // time will be potentially dropped on system shutdown.
539 if (interval > kUseMonitorIntervalMax)
540 interval = kUseMonitorIntervalMax;
541
542 if (backoff) {
543 // Back-off mode is used by the use monitor to reschedule itself
544 // with exponential back-off in time. This mode doesn't create a
545 // new timeout source if the new interval is the same as the old
546 // one. Also, if a new timeout source is created, the old one is
547 // not destroyed explicitly here -- it will be destroyed by GLib
548 // when the monitor returns FALSE (see UseMonitor and
549 // UseMonitorStatic).
550 if (interval == usemon_interval_)
551 return false;
552 } else {
553 UnscheduleUseMonitor();
554 }
555
556 // Schedules a new use monitor for |interval| seconds from now.
557 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
558 usemon_source_ = g_timeout_source_new_seconds(interval);
559 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
560 NULL); // No destroy notification.
561 g_source_attach(usemon_source_,
562 NULL); // Default context.
563 usemon_interval_ = interval;
564 return true;
565}
566
567void MetricsDaemon::UnscheduleUseMonitor() {
568 // If there's a use monitor scheduled already, destroys it.
569 if (usemon_source_ == NULL)
570 return;
571
572 DLOG(INFO) << "destroying use monitor";
573 g_source_destroy(usemon_source_);
574 usemon_source_ = NULL;
575 usemon_interval_ = 0;
576}
577
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700578void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800579 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700580 VmStatsReadStats(&page_faults_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800581 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700582 stats_state_ = kStatsLong;
583 stats_initial_time_ = GetActiveTime();
584 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700585 LOG(WARNING) << "not collecting disk stats";
586 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700587 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700588 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800589}
590
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700591void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800592 if (testing_) {
593 return;
594 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700595 g_timeout_add_seconds(wait, StatsCallbackStatic, this);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800596}
597
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700598bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800599 long int* write_sectors) {
600 int nchars;
601 int nitems;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700602 bool success = false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800603 char line[200];
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700604 if (diskstats_path_.empty()) {
605 return false;
606 }
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800607 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800608 if (file < 0) {
609 PLOG(WARNING) << "cannot open " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700610 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800611 }
612 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
613 if (nchars < 0) {
614 PLOG(WARNING) << "cannot read from " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700615 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800616 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700617 LOG_IF(WARNING, nchars == sizeof(line))
618 << "line too long in " << diskstats_path_;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800619 line[nchars] = '\0';
620 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
621 read_sectors, write_sectors);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700622 if (nitems == 2) {
623 success = true;
624 } else {
625 LOG(WARNING) << "found " << nitems << " items in "
626 << diskstats_path_ << ", expected 2";
627 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800628 }
629 HANDLE_EINTR(close(file));
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700630 return success;
631}
632
633bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
634 static const char kPageFaultSearchString[] = "\npgmajfault ";
635 bool success = false;
636 /* Each line in the file has the form
637 * <ID> <VALUE>
638 * for instance:
639 * nr_free_pages 213427
640 */
641 char* s = strstr(stats, kPageFaultSearchString);
642 if (s == NULL) {
643 LOG(WARNING) << "cannot find page fault entry in vmstats";
644 } else {
645 char* endp;
646 /* Skip <ID> and space. Don't count the terminating null. */
647 s += sizeof(kPageFaultSearchString) - 1;
648 *page_faults = strtol(s, &endp, 10);
649 if (*endp == '\n') {
650 success = true;
651 } else {
652 LOG(WARNING) << "error parsing vmstats";
653 }
654 }
655 return success;
656}
657
658bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
659 char buffer[4000];
660 int nchars;
661 int success = false;
662 if (testing_) {
663 return false;
664 }
665 int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
666 if (file < 0) {
667 PLOG(WARNING) << "cannot open " << vmstats_path_;
668 return false;
669 }
670 nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
671 LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
672 << "file too large in " << vmstats_path_;
673 if (nchars < 0) {
674 PLOG(WARNING) << "cannot read from " << vmstats_path_;
675 } else if (nchars == 0) {
676 LOG(WARNING) << vmstats_path_ << " is empty";
677 } else {
678 buffer[nchars] = '\0';
679 success = VmStatsParseStats(buffer, page_faults);
680 }
681 HANDLE_EINTR(close(file));
682 return success;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800683}
684
685// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700686gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
687 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800688 return false; // one-time callback
689}
690
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700691// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700692
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700693void MetricsDaemon::StatsCallback() {
694 long int read_sectors_now, write_sectors_now, page_faults_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700695 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700696 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700697 if (testing_) {
698 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700699 delta_time = stats_state_ == kStatsShort ?
700 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700701 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700702 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
703 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700704 int delta_read = read_sectors_now - read_sectors_;
705 int delta_write = write_sectors_now - write_sectors_;
706 int read_sectors_per_second = delta_read / delta_time;
707 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700708 bool vmstats_success = VmStatsReadStats(&page_faults_now);
709 int delta_faults = page_faults_now - page_faults_;
710 int page_faults_per_second = delta_faults / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800711
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700712 switch (stats_state_) {
713 case kStatsShort:
714 if (diskstats_success) {
715 SendMetric(kMetricReadSectorsShortName,
716 read_sectors_per_second,
717 1,
718 kMetricSectorsIOMax,
719 kMetricSectorsBuckets);
720 SendMetric(kMetricWriteSectorsShortName,
721 write_sectors_per_second,
722 1,
723 kMetricSectorsIOMax,
724 kMetricSectorsBuckets);
725 }
726 if (vmstats_success) {
727 SendMetric(kMetricPageFaultsShortName,
728 page_faults_per_second,
729 1,
730 kMetricPageFaultsMax,
731 kMetricPageFaultsBuckets);
732 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800733 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700734 stats_state_ = kStatsLong;
735 ScheduleStatsCallback(kMetricStatsLongInterval -
736 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800737 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700738 case kStatsLong:
739 if (diskstats_success) {
740 SendMetric(kMetricReadSectorsLongName,
741 read_sectors_per_second,
742 1,
743 kMetricSectorsIOMax,
744 kMetricSectorsBuckets);
745 SendMetric(kMetricWriteSectorsLongName,
746 write_sectors_per_second,
747 1,
748 kMetricSectorsIOMax,
749 kMetricSectorsBuckets);
750 // Reset sector counters.
751 read_sectors_ = read_sectors_now;
752 write_sectors_ = write_sectors_now;
753 }
754 if (vmstats_success) {
755 SendMetric(kMetricPageFaultsLongName,
756 page_faults_per_second,
757 1,
758 kMetricPageFaultsMax,
759 kMetricPageFaultsBuckets);
760 page_faults_ = page_faults_now;
761 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700762 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700763 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800764 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700765 stats_state_ = kStatsShort;
766 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800767 break;
768 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700769 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800770 }
771}
772
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700773void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
774 if (testing_) {
775 return;
776 }
777 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
778}
779
780// static
781gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
782 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
783}
784
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700785bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700786 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700787 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700788 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700789 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
790 return false;
791 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700792 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700793}
794
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700795bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700796 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700797 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
798 { "MemFree", "MemFree" },
799 { "Buffers", "Buffers" },
800 { "Cached", "Cached" },
801 // { "SwapCached", "SwapCached" },
802 { "Active", "Active" },
803 { "Inactive", "Inactive" },
804 { "ActiveAnon", "Active(anon)" },
805 { "InactiveAnon", "Inactive(anon)" },
806 { "ActiveFile" , "Active(file)" },
807 { "InactiveFile", "Inactive(file)" },
808 { "Unevictable", "Unevictable", 1 },
809 // { "Mlocked", "Mlocked" },
810 // { "SwapTotal", "SwapTotal" },
811 // { "SwapFree", "SwapFree" },
812 // { "Dirty", "Dirty" },
813 // { "Writeback", "Writeback" },
814 { "AnonPages", "AnonPages" },
815 { "Mapped", "Mapped" },
816 { "Shmem", "Shmem", 1 },
817 { "Slab", "Slab", 1 },
818 // { "SReclaimable", "SReclaimable" },
819 // { "SUnreclaim", "SUnreclaim" },
820 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700821 vector<MeminfoRecord> fields(fields_array,
822 fields_array + arraysize(fields_array));
823 if (!FillMeminfo(meminfo_raw, &fields)) {
824 return false;
825 }
826 int total_memory = fields[0].value;
827 if (total_memory == 0) {
828 // this "cannot happen"
829 LOG(WARNING) << "borked meminfo parser";
830 return false;
831 }
832 // Send all fields retrieved, except total memory.
833 for (unsigned int i = 1; i < fields.size(); i++) {
834 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
835 if (fields[i].log_scale) {
836 // report value in kbytes, log scale, 4Gb max
837 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
838 } else {
839 // report value as percent of total memory
840 int percent = fields[i].value * 100 / total_memory;
841 SendLinearMetric(metrics_name, percent, 100, 101);
842 }
843 }
844 return true;
845}
846
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700847bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
848 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700849 vector<string> lines;
850 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700851
852 // Scan meminfo output and collect field values. Each field name has to
853 // match a meminfo entry (case insensitive) after removing non-alpha
854 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700855 unsigned int ifield = 0;
856 for (unsigned int iline = 0;
857 iline < nlines && ifield < fields->size();
858 iline++) {
859 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700860 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700861 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
862 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700863 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700864 (*fields)[ifield].value =
865 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700866 if (*rest != '\0') {
867 LOG(WARNING) << "missing meminfo value";
868 return false;
869 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700870 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700871 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700872 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700873 if (ifield < fields->size()) {
874 // End of input reached while scanning.
875 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
876 << " and following";
877 return false;
878 }
879 return true;
880}
881
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700882void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700883 double time_elapsed) {
884 if (testing_) {
885 return;
886 }
887 int interval = kMemuseIntervals[memuse_interval_index_];
888 int wait;
889 if (new_callback) {
890 memuse_initial_time_ = GetActiveTime();
891 wait = interval;
892 } else {
893 wait = ceil(interval - time_elapsed); // round up
894 }
895 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
896}
897
898// static
899gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
900 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
901 daemon->MemuseCallback();
902 return false;
903}
904
905void MetricsDaemon::MemuseCallback() {
906 // Since we only care about active time (i.e. uptime minus sleep time) but
907 // the callbacks are driven by real time (uptime), we check if we should
908 // reschedule this callback due to intervening sleep periods.
909 double now = GetActiveTime();
910 double active_time = now - memuse_initial_time_;
911 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
912 // Not enough active time has passed. Reschedule the callback.
913 ScheduleMemuseCallback(false, active_time);
914 } else {
915 // Enough active time has passed. Do the work, and (if we succeed) see if
916 // we need to do more.
917 if (MemuseCallbackWork() &&
918 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
919 memuse_interval_index_++;
920 ScheduleMemuseCallback(true, 0);
921 }
922 }
923}
924
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700925bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700926 string meminfo_raw;
927 const FilePath meminfo_path("/proc/meminfo");
928 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
929 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
930 return false;
931 }
932 return ProcessMemuse(meminfo_raw);
933}
934
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700935bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700936 static const MeminfoRecord fields_array[] = {
937 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
938 { "ActiveAnon", "Active(anon)" },
939 { "InactiveAnon", "Inactive(anon)" },
940 };
941 vector<MeminfoRecord> fields(fields_array,
942 fields_array + arraysize(fields_array));
943 if (!FillMeminfo(meminfo_raw, &fields)) {
944 return false;
945 }
946 int total = fields[0].value;
947 int active_anon = fields[1].value;
948 int inactive_anon = fields[2].value;
949 if (total == 0) {
950 // this "cannot happen"
951 LOG(WARNING) << "borked meminfo parser";
952 return false;
953 }
954 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
955 memuse_interval_index_);
956 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
957 100, 101);
958 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700959}
960
Darin Petkovf1e85e42010-06-10 15:59:53 -0700961// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700962void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700963 if (count <= 0)
964 return;
965
Darin Petkovf1e85e42010-06-10 15:59:53 -0700966 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
967 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
968 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
969 kMetricDailyUseTimeMin,
970 kMetricDailyUseTimeMax,
971 kMetricDailyUseTimeBuckets);
972}
973
Darin Petkov38d5cb02010-06-24 12:10:26 -0700974void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700975 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700976 DLOG(INFO) << "received metric: " << name << " " << sample << " "
977 << min << " " << max << " " << nbuckets;
978 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700979}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700980
981void MetricsDaemon::SendLinearMetric(const string& name, int sample,
982 int max, int nbuckets) {
983 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
984 << max << " " << nbuckets;
985 // TODO(semenzato): add a proper linear histogram to the Chrome external
986 // metrics API.
987 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
988 metrics_lib_->SendEnumToUMA(name, sample, max);
989}