blob: e9031ab7a3adc3262db5edc3a3f60a95be71f47c [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>
Ken Mixter4c5daa42010-08-26 18:35:06 -07008#include <string.h>
Darin Petkov65b01462010-04-14 13:32:20 -07009
Darin Petkov38d5cb02010-06-24 12:10:26 -070010#include <base/file_util.h>
Darin Petkov65b01462010-04-14 13:32:20 -070011#include <base/logging.h>
Luigi Semenzato29c7ef92011-04-12 14:12:35 -070012#include <base/string_util.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070013#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070014
Darin Petkovf1e85e42010-06-10 15:59:53 -070015#include "counter.h"
16
Darin Petkovf27f0362010-06-04 13:14:19 -070017using base::Time;
18using base::TimeDelta;
19using base::TimeTicks;
Darin Petkov38d5cb02010-06-24 12:10:26 -070020using std::string;
Darin Petkovf27f0362010-06-04 13:14:19 -070021
Darin Petkov703ec972010-04-27 11:02:18 -070022#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov1bb904e2010-06-16 15:58:06 -070023#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter"
David James6bf6e252010-06-06 18:52:40 -070024#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager"
Darin Petkov41e06232010-05-03 16:45:37 -070025#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface"
26
Darin Petkov41e06232010-05-03 16:45:37 -070027static const int kSecondsPerMinute = 60;
28static const int kMinutesPerHour = 60;
29static const int kHoursPerDay = 24;
30static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070031static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
32static const int kDaysPerWeek = 7;
33static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070034
35// The daily use monitor is scheduled to a 1-minute interval after
36// initial user activity and then it's exponentially backed off to
37// 10-minute intervals. Although not required, the back off is
38// implemented because the histogram buckets are spaced exponentially
39// anyway and to avoid too frequent metrics daemon process wake-ups
40// and file I/O.
41static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
42static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070043
Ken Mixterccd84c02010-08-16 19:57:13 -070044const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
45static const char kUncleanShutdownDetectedFile[] =
46 "/tmp/unclean-shutdown-detected";
47
Ken Mixter4c5daa42010-08-26 18:35:06 -070048// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070049const char MetricsDaemon::kMetricDailyUseTimeName[] =
50 "Logging.DailyUseTime";
51const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
52const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
53const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
54
Ken Mixterccd84c02010-08-16 19:57:13 -070055// crash interval metrics
56const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
57 "Logging.KernelCrashInterval";
58const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
59 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070060const char MetricsDaemon::kMetricUserCrashIntervalName[] =
61 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070062
63const int MetricsDaemon::kMetricCrashIntervalMin = 1;
64const int MetricsDaemon::kMetricCrashIntervalMax =
65 4 * kSecondsPerWeek;
66const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
67
68// crash frequency metrics
69const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
70 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070071const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
72 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070073const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
74 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070075const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
76 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070077const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
78 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070079const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
80 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070081const char MetricsDaemon::kMetricUserCrashesDailyName[] =
82 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070083const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
84 "Logging.UserCrashesWeekly";
85const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
86const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
87const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070088
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080089// disk stats metrics
90
91// The {Read,Write}Sectors numbers are in sectors/second.
92// A sector is usually 512 bytes.
93
94const char MetricsDaemon::kMetricReadSectorsLongName[] =
95 "Platform.ReadSectorsLong";
96const char MetricsDaemon::kMetricWriteSectorsLongName[] =
97 "Platform.WriteSectorsLong";
98const char MetricsDaemon::kMetricReadSectorsShortName[] =
99 "Platform.ReadSectorsShort";
100const char MetricsDaemon::kMetricWriteSectorsShortName[] =
101 "Platform.WriteSectorsShort";
102
103const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds
104const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds
105
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700106const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
107
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800108// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
109// sectors.
110const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
111const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
112
Ken Mixter4c5daa42010-08-26 18:35:06 -0700113// persistent metrics path
114const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700115
Darin Petkov1bb904e2010-06-16 15:58:06 -0700116
Darin Petkov703ec972010-04-27 11:02:18 -0700117// static
Darin Petkov41e06232010-05-03 16:45:37 -0700118const char* MetricsDaemon::kDBusMatches_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700119 "type='signal',"
Darin Petkov1bb904e2010-06-16 15:58:06 -0700120 "interface='" DBUS_IFACE_CRASH_REPORTER "',"
121 "path='/',"
122 "member='UserCrash'",
123
124 "type='signal',"
Darin Petkov703ec972010-04-27 11:02:18 -0700125 "interface='" DBUS_IFACE_POWER_MANAGER "',"
Benson Leung53faeb02010-06-08 15:59:13 -0700126 "path='/'",
Darin Petkov41e06232010-05-03 16:45:37 -0700127
128 "type='signal',"
129 "sender='org.chromium.SessionManager',"
130 "interface='" DBUS_IFACE_SESSION_MANAGER "',"
131 "path='/org/chromium/SessionManager',"
132 "member='SessionStateChanged'",
Darin Petkov703ec972010-04-27 11:02:18 -0700133};
134
135// static
Darin Petkov41e06232010-05-03 16:45:37 -0700136const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700137#define STATE(name, capname) #name,
138#include "power_states.h"
139};
140
Darin Petkov41e06232010-05-03 16:45:37 -0700141// static
Darin Petkov41e06232010-05-03 16:45:37 -0700142const char* MetricsDaemon::kSessionStates_[] = {
143#define STATE(name, capname) #name,
144#include "session_states.h"
145};
146
Darin Petkovf1e85e42010-06-10 15:59:53 -0700147MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700148 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700149 session_state_(kUnknownSessionState),
150 user_active_(false),
151 usemon_interval_(0),
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800152 usemon_source_(NULL) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700153
Ken Mixter4c5daa42010-08-26 18:35:06 -0700154MetricsDaemon::~MetricsDaemon() {
155 DeleteFrequencyCounters();
156}
157
158void MetricsDaemon::DeleteFrequencyCounters() {
159 for (FrequencyCounters::iterator i = frequency_counters_.begin();
160 i != frequency_counters_.end(); ++i) {
161 delete i->second;
162 i->second = NULL;
163 }
164}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700165
Darin Petkov2ccef012010-05-05 16:06:37 -0700166void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700167 if (run_as_daemon && daemon(0, 0) != 0)
168 return;
169
Ken Mixterccd84c02010-08-16 19:57:13 -0700170 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
171 ProcessKernelCrash();
172 }
173
174 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
175 ProcessUncleanShutdown();
176 }
177
Darin Petkov38d5cb02010-06-24 12:10:26 -0700178 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700179}
180
Ken Mixter4c5daa42010-08-26 18:35:06 -0700181FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
182 return FilePath(kMetricsPath).Append(histogram_name);
183}
184
185void MetricsDaemon::ConfigureCrashIntervalReporter(
186 const char* histogram_name,
187 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
188 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
189 FilePath file_path = GetHistogramPath(histogram_name);
190 (*reporter)->Init(file_path.value().c_str(),
191 histogram_name,
192 kMetricCrashIntervalMin,
193 kMetricCrashIntervalMax,
194 kMetricCrashIntervalBuckets);
195}
196
197void MetricsDaemon::ConfigureCrashFrequencyReporter(
198 const char* histogram_name) {
199 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
200 new chromeos_metrics::TaggedCounterReporter());
201 FilePath file_path = GetHistogramPath(histogram_name);
202 reporter->Init(file_path.value().c_str(),
203 histogram_name,
204 kMetricCrashFrequencyMin,
205 kMetricCrashFrequencyMax,
206 kMetricCrashFrequencyBuckets);
207 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
208 new chromeos_metrics::FrequencyCounter());
209 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
210 chromeos_metrics::kSecondsPerWeek :
211 chromeos_metrics::kSecondsPerDay;
212 new_counter->Init(
213 static_cast<chromeos_metrics::TaggedCounterInterface*>(
214 reporter.release()),
215 cycle_duration);
216 frequency_counters_[histogram_name] = new_counter.release();
217}
218
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800219void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800220 string diskstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700221 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700222 DCHECK(metrics_lib != NULL);
223 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700224 chromeos_metrics::TaggedCounterReporter::
225 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700226
227 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700228 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700229 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700230
Ken Mixter4c5daa42010-08-26 18:35:06 -0700231 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
232 &kernel_crash_interval_);
233 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
234 &unclean_shutdown_interval_);
235 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
236 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700237
Ken Mixter4c5daa42010-08-26 18:35:06 -0700238 DeleteFrequencyCounters();
239 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700240 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
241 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
242 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
243 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
244 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
245 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
246 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700247
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800248 // Don't attempt to collect disk stats if there is no disk stats file.
249 if (!diskstats_path.empty()) {
250 diskstats_path_ = diskstats_path;
251 DiskStatsReporterInit();
252 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800253
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700254 // Start collecting meminfo stats.
255 ScheduleMeminfoCallback(kMetricMeminfoInterval);
256
Darin Petkov2ccef012010-05-05 16:06:37 -0700257 // Don't setup D-Bus and GLib in test mode.
258 if (testing)
259 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700260
Darin Petkov703ec972010-04-27 11:02:18 -0700261 g_thread_init(NULL);
262 g_type_init();
263 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700264
Darin Petkov703ec972010-04-27 11:02:18 -0700265 DBusError error;
266 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700267
David James3b3add52010-06-04 15:01:19 -0700268 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700269 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
270 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700271
Darin Petkov703ec972010-04-27 11:02:18 -0700272 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700273
Darin Petkov703ec972010-04-27 11:02:18 -0700274 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700275 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700276 const char* match = kDBusMatches_[m];
277 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700278 dbus_bus_add_match(connection, match, &error);
279 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
280 "unable to add a match: " << SAFE_MESSAGE(error);
281 }
282
283 // Adds the D-Bus filter routine to be called back whenever one of
284 // the registered D-Bus matches is successful. The daemon is not
285 // activated for D-Bus messages that don't match.
286 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700287}
288
289void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700290 GMainLoop* loop = g_main_loop_new(NULL, false);
291 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700292}
293
Darin Petkov703ec972010-04-27 11:02:18 -0700294// static
295DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
296 DBusMessage* message,
297 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700298 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700299 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700300
301 int message_type = dbus_message_get_type(message);
302 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700303 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700304 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
305 }
306
307 // Signal messages always have interfaces.
308 const char* interface = dbus_message_get_interface(message);
309 CHECK(interface != NULL);
310
311 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
312
313 DBusMessageIter iter;
314 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700315 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
316 CHECK(strcmp(dbus_message_get_member(message),
317 "UserCrash") == 0);
318 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700319 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700320 const char* member = dbus_message_get_member(message);
321 if (strcmp(member, "ScreenIsLocked") == 0) {
322 daemon->SetUserActiveState(false, now);
323 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
324 daemon->SetUserActiveState(true, now);
325 } else if (strcmp(member, "PowerStateChanged") == 0) {
326 char* state_name;
327 dbus_message_iter_get_basic(&iter, &state_name);
328 daemon->PowerStateChanged(state_name, now);
329 }
Darin Petkov41e06232010-05-03 16:45:37 -0700330 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
331 CHECK(strcmp(dbus_message_get_member(message),
332 "SessionStateChanged") == 0);
333
David James3b3add52010-06-04 15:01:19 -0700334 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700335 dbus_message_iter_get_basic(&iter, &state_name);
336 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700337 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700338 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700339 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
340 }
341
342 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700343}
344
Darin Petkovf27f0362010-06-04 13:14:19 -0700345void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700346 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700347 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700348
349 if (power_state_ != kPowerStateOn)
350 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700351}
352
353MetricsDaemon::PowerState
354MetricsDaemon::LookupPowerState(const char* state_name) {
355 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700356 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700357 return static_cast<PowerState>(i);
358 }
359 }
Darin Petkov41e06232010-05-03 16:45:37 -0700360 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700361 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700362}
363
Darin Petkovf27f0362010-06-04 13:14:19 -0700364void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700365 DLOG(INFO) << "user session state: " << state_name;
366 session_state_ = LookupSessionState(state_name);
367 SetUserActiveState(session_state_ == kSessionStateStarted, now);
368}
369
370MetricsDaemon::SessionState
371MetricsDaemon::LookupSessionState(const char* state_name) {
372 for (int i = 0; i < kNumberSessionStates; i++) {
373 if (strcmp(state_name, kSessionStates_[i]) == 0) {
374 return static_cast<SessionState>(i);
375 }
376 }
377 DLOG(WARNING) << "unknown user session state: " << state_name;
378 return kUnknownSessionState;
379}
380
Darin Petkovf27f0362010-06-04 13:14:19 -0700381void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700382 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
383
384 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700385 // the day since Epoch, and logs the usage data. Guards against the
386 // time jumping back and forth due to the user changing it by
387 // discarding the new use time.
388 int seconds = 0;
389 if (user_active_ && now > user_active_last_) {
390 TimeDelta since_active = now - user_active_last_;
391 if (since_active < TimeDelta::FromSeconds(
392 kUseMonitorIntervalMax + kSecondsPerMinute)) {
393 seconds = static_cast<int>(since_active.InSeconds());
394 }
395 }
396 TimeDelta since_epoch = now - Time();
397 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700398 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700399 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700400 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700401
Ken Mixter4c5daa42010-08-26 18:35:06 -0700402 // Flush finished cycles of all frequency counters.
403 for (FrequencyCounters::iterator i = frequency_counters_.begin();
404 i != frequency_counters_.end(); ++i) {
405 i->second->FlushFinishedCycles();
406 }
407
Darin Petkov41e06232010-05-03 16:45:37 -0700408 // Schedules a use monitor on inactive->active transitions and
409 // unschedules it on active->inactive transitions.
410 if (!user_active_ && active)
411 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
412 else if (user_active_ && !active)
413 UnscheduleUseMonitor();
414
415 // Remembers the current active state and the time of the last
416 // activity update.
417 user_active_ = active;
418 user_active_last_ = now;
419}
420
Darin Petkov1bb904e2010-06-16 15:58:06 -0700421void MetricsDaemon::ProcessUserCrash() {
422 // Counts the active use time up to now.
423 SetUserActiveState(user_active_, Time::Now());
424
425 // Reports the active use time since the last crash and resets it.
426 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700427
Ken Mixter4c5daa42010-08-26 18:35:06 -0700428 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
429 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
430 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
431 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700432}
433
Darin Petkov38d5cb02010-06-24 12:10:26 -0700434void MetricsDaemon::ProcessKernelCrash() {
435 // Counts the active use time up to now.
436 SetUserActiveState(user_active_, Time::Now());
437
438 // Reports the active use time since the last crash and resets it.
439 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700440
Ken Mixter4c5daa42010-08-26 18:35:06 -0700441 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
442 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
443 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
444 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700445}
446
Ken Mixterccd84c02010-08-16 19:57:13 -0700447void MetricsDaemon::ProcessUncleanShutdown() {
448 // Counts the active use time up to now.
449 SetUserActiveState(user_active_, Time::Now());
450
451 // Reports the active use time since the last crash and resets it.
452 unclean_shutdown_interval_->Flush();
453
Ken Mixter4c5daa42010-08-26 18:35:06 -0700454 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
455 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
456 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
457 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700458}
459
460bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700461 FilePath crash_detected(crash_file);
462 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700463 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700464
465 // Deletes the crash-detected file so that the daemon doesn't report
466 // another kernel crash in case it's restarted.
467 file_util::Delete(crash_detected,
468 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700469 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700470}
471
Darin Petkov41e06232010-05-03 16:45:37 -0700472// static
473gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
474 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
475}
476
477bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700478 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700479
480 // If a new monitor source/instance is scheduled, returns false to
481 // tell GLib to destroy this monitor source/instance. Returns true
482 // otherwise to keep calling back this monitor.
483 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
484}
485
486bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
487{
Darin Petkov2ccef012010-05-05 16:06:37 -0700488 if (testing_)
489 return false;
490
Darin Petkov41e06232010-05-03 16:45:37 -0700491 // Caps the interval -- the bigger the interval, the more active use
492 // time will be potentially dropped on system shutdown.
493 if (interval > kUseMonitorIntervalMax)
494 interval = kUseMonitorIntervalMax;
495
496 if (backoff) {
497 // Back-off mode is used by the use monitor to reschedule itself
498 // with exponential back-off in time. This mode doesn't create a
499 // new timeout source if the new interval is the same as the old
500 // one. Also, if a new timeout source is created, the old one is
501 // not destroyed explicitly here -- it will be destroyed by GLib
502 // when the monitor returns FALSE (see UseMonitor and
503 // UseMonitorStatic).
504 if (interval == usemon_interval_)
505 return false;
506 } else {
507 UnscheduleUseMonitor();
508 }
509
510 // Schedules a new use monitor for |interval| seconds from now.
511 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
512 usemon_source_ = g_timeout_source_new_seconds(interval);
513 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
514 NULL); // No destroy notification.
515 g_source_attach(usemon_source_,
516 NULL); // Default context.
517 usemon_interval_ = interval;
518 return true;
519}
520
521void MetricsDaemon::UnscheduleUseMonitor() {
522 // If there's a use monitor scheduled already, destroys it.
523 if (usemon_source_ == NULL)
524 return;
525
526 DLOG(INFO) << "destroying use monitor";
527 g_source_destroy(usemon_source_);
528 usemon_source_ = NULL;
529 usemon_interval_ = 0;
530}
531
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800532void MetricsDaemon::DiskStatsReporterInit() {
533 DiskStatsReadStats(&read_sectors_, &write_sectors_);
534 // The first time around just run the long stat, so we don't delay boot.
535 diskstats_state_ = kDiskStatsLong;
536 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval);
537}
538
539void MetricsDaemon::ScheduleDiskStatsCallback(int wait) {
540 if (testing_) {
541 return;
542 }
543 g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this);
544}
545
546void MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
547 long int* write_sectors) {
548 int nchars;
549 int nitems;
550 char line[200];
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800551 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800552 if (file < 0) {
553 PLOG(WARNING) << "cannot open " << diskstats_path_;
554 return;
555 }
556 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
557 if (nchars < 0) {
558 PLOG(WARNING) << "cannot read from " << diskstats_path_;
559 } else {
560 LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in "
561 << diskstats_path_;
562 line[nchars] = '\0';
563 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
564 read_sectors, write_sectors);
565 LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in "
566 << diskstats_path_ << ", expected 2";
567 }
568 HANDLE_EINTR(close(file));
569}
570
571// static
572gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) {
573 (static_cast<MetricsDaemon*>(handle))->DiskStatsCallback();
574 return false; // one-time callback
575}
576
577void MetricsDaemon::DiskStatsCallback() {
578 long int read_sectors_now, write_sectors_now;
579 DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
580
581 switch (diskstats_state_) {
582 case kDiskStatsShort:
583 SendMetric(kMetricReadSectorsShortName,
584 (int) (read_sectors_now - read_sectors_) /
585 kMetricDiskStatsShortInterval,
586 1,
587 kMetricSectorsIOMax,
588 kMetricSectorsBuckets);
589 SendMetric(kMetricWriteSectorsShortName,
590 (int) (write_sectors_now - write_sectors_) /
591 kMetricDiskStatsShortInterval,
592 1,
593 kMetricSectorsIOMax,
594 kMetricSectorsBuckets);
595 // Schedule long callback.
596 diskstats_state_ = kDiskStatsLong;
597 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval -
598 kMetricDiskStatsShortInterval);
599 break;
600 case kDiskStatsLong:
601 SendMetric(kMetricReadSectorsLongName,
602 (int) (read_sectors_now - read_sectors_) /
603 kMetricDiskStatsLongInterval,
604 1,
605 kMetricSectorsIOMax,
606 kMetricSectorsBuckets);
607 SendMetric(kMetricWriteSectorsLongName,
608 (int) (write_sectors_now - write_sectors_) /
609 kMetricDiskStatsLongInterval,
610 1,
611 kMetricSectorsIOMax,
612 kMetricSectorsBuckets);
613 // Reset sector counters
614 read_sectors_ = read_sectors_now;
615 write_sectors_ = write_sectors_now;
616 // Schedule short callback.
617 diskstats_state_ = kDiskStatsShort;
618 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval);
619 break;
620 default:
621 LOG(FATAL) << "Invalid disk stats state";
622 }
623}
624
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700625void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
626 if (testing_) {
627 return;
628 }
629 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
630}
631
632// static
633gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
634 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
635}
636
637gboolean MetricsDaemon::MeminfoCallback() {
638 std::string meminfo;
639 const FilePath meminfo_path("/proc/meminfo");
640 if (!file_util::ReadFileToString(meminfo_path, &meminfo)) {
641 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
642 return false;
643 }
644 return ProcessMeminfo(meminfo);
645}
646
647gboolean MetricsDaemon::ProcessMeminfo(std::string meminfo) {
648 // This array has one element for every item of /proc/meminfo that we want to
649 // report to UMA. They must be listed in the same order in which
650 // /proc/meminfo prints them.
651 struct {
652 const char* name; // print name
653 const char* match; // string to match in output of /proc/meminfo
654 int log_scale; // report with log scale instead of linear percent
655 } fields[] = {
656 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
657 { "MemFree", "MemFree" },
658 { "Buffers", "Buffers" },
659 { "Cached", "Cached" },
660 // { "SwapCached", "SwapCached" },
661 { "Active", "Active" },
662 { "Inactive", "Inactive" },
663 { "ActiveAnon", "Active(anon)" },
664 { "InactiveAnon", "Inactive(anon)" },
665 { "ActiveFile" , "Active(file)" },
666 { "InactiveFile", "Inactive(file)" },
667 { "Unevictable", "Unevictable", 1 },
668 // { "Mlocked", "Mlocked" },
669 // { "SwapTotal", "SwapTotal" },
670 // { "SwapFree", "SwapFree" },
671 // { "Dirty", "Dirty" },
672 // { "Writeback", "Writeback" },
673 { "AnonPages", "AnonPages" },
674 { "Mapped", "Mapped" },
675 { "Shmem", "Shmem", 1 },
676 { "Slab", "Slab", 1 },
677 // { "SReclaimable", "SReclaimable" },
678 // { "SUnreclaim", "SUnreclaim" },
679 };
680 // arraysize doesn't work here, probably can't handle anonymous structs
681 const int nfields = sizeof(fields) / sizeof(fields[0]);
682 int total_memory = 0;
683 std::vector<std::string> lines;
684 int nlines = Tokenize(meminfo, "\n", &lines);
685
686 // Scan meminfo output and collect field values. Each field name has to
687 // match a meminfo entry (case insensitive) after removing non-alpha
688 // characters from the entry.
689 int i = 0;
690 int iline = 0;
691 for (;;) {
692 if (i == nfields) {
693 // all fields are matched
694 return true;
695 }
696 if (iline == nlines) {
697 // end of input reached while scanning
698 LOG(WARNING) << "cannot find field " << fields[i].match
699 << " and following";
700 return false;
701 }
702
703 std::vector<std::string> tokens;
704 Tokenize(lines[iline], ": ", &tokens);
705
706 if (strcmp(fields[i].match, tokens[0].c_str()) == 0) {
707 // name matches: parse value and report
708 int meminfo_value;
709 char metrics_name[128];
710 char* rest;
711 meminfo_value = static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
712 if (*rest != '\0') {
713 LOG(WARNING) << "missing meminfo value";
714 return false;
715 }
716 if (i == 0) {
717 // special case: total memory
718 total_memory = meminfo_value;
719 } else {
720 snprintf(metrics_name, sizeof(metrics_name),
721 "Platform.Meminfo%s", fields[i].name);
722 if (fields[i].log_scale) {
723 // report value in kbytes, log scale, 4Gb max
724 SendMetric(metrics_name, meminfo_value, 1, 4 * 1000 * 1000, 100);
725 } else {
726 // report value as percent of total memory
727 if (total_memory == 0) {
728 // this "cannot happen"
729 LOG(WARNING) << "borked meminfo parser";
730 return false;
731 }
732 int percent = meminfo_value * 100 / total_memory;
733 SendLinearMetric(metrics_name, percent, 100, 101);
734 }
735 }
736 // start looking for next field
737 i++;
738 }
739 iline++;
740 }
741}
742
Darin Petkovf1e85e42010-06-10 15:59:53 -0700743// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700744void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700745 if (count <= 0)
746 return;
747
Darin Petkovf1e85e42010-06-10 15:59:53 -0700748 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
749 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
750 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
751 kMetricDailyUseTimeMin,
752 kMetricDailyUseTimeMax,
753 kMetricDailyUseTimeBuckets);
754}
755
Darin Petkov38d5cb02010-06-24 12:10:26 -0700756void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700757 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700758 DLOG(INFO) << "received metric: " << name << " " << sample << " "
759 << min << " " << max << " " << nbuckets;
760 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700761}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700762
763void MetricsDaemon::SendLinearMetric(const string& name, int sample,
764 int max, int nbuckets) {
765 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
766 << max << " " << nbuckets;
767 // TODO(semenzato): add a proper linear histogram to the Chrome external
768 // metrics API.
769 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
770 metrics_lib_->SendEnumToUMA(name, sample, max);
771}