blob: 1e15e1dde7396490d42a473d4f94074583197350 [file] [log] [blame]
Darin Petkov65b01462010-04-14 13:32:20 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// 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>
Ken Mixter4c5daa42010-08-26 18:35:06 -070012#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070013
Darin Petkovf1e85e42010-06-10 15:59:53 -070014#include "counter.h"
15
Darin Petkovf27f0362010-06-04 13:14:19 -070016using base::Time;
17using base::TimeDelta;
18using base::TimeTicks;
Darin Petkov38d5cb02010-06-24 12:10:26 -070019using std::string;
Darin Petkovf27f0362010-06-04 13:14:19 -070020
Darin Petkov703ec972010-04-27 11:02:18 -070021#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov1bb904e2010-06-16 15:58:06 -070022#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter"
David James6bf6e252010-06-06 18:52:40 -070023#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager"
Darin Petkov41e06232010-05-03 16:45:37 -070024#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface"
25
Darin Petkov41e06232010-05-03 16:45:37 -070026static const int kSecondsPerMinute = 60;
27static const int kMinutesPerHour = 60;
28static const int kHoursPerDay = 24;
29static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070030static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
31static const int kDaysPerWeek = 7;
32static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070033
34// The daily use monitor is scheduled to a 1-minute interval after
35// initial user activity and then it's exponentially backed off to
36// 10-minute intervals. Although not required, the back off is
37// implemented because the histogram buckets are spaced exponentially
38// anyway and to avoid too frequent metrics daemon process wake-ups
39// and file I/O.
40static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
41static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070042
Ken Mixterccd84c02010-08-16 19:57:13 -070043const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
44static const char kUncleanShutdownDetectedFile[] =
45 "/tmp/unclean-shutdown-detected";
46
Ken Mixter4c5daa42010-08-26 18:35:06 -070047// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070048const char MetricsDaemon::kMetricDailyUseTimeName[] =
49 "Logging.DailyUseTime";
50const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
51const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
52const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
53
Ken Mixterccd84c02010-08-16 19:57:13 -070054// crash interval metrics
55const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
56 "Logging.KernelCrashInterval";
57const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
58 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070059const char MetricsDaemon::kMetricUserCrashIntervalName[] =
60 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070061
62const int MetricsDaemon::kMetricCrashIntervalMin = 1;
63const int MetricsDaemon::kMetricCrashIntervalMax =
64 4 * kSecondsPerWeek;
65const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
66
67// crash frequency metrics
68const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
69 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070070const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
71 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070072const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
73 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070074const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
75 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070076const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
77 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070078const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
79 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070080const char MetricsDaemon::kMetricUserCrashesDailyName[] =
81 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070082const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
83 "Logging.UserCrashesWeekly";
84const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
85const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
86const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070087
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080088// disk stats metrics
89
90// The {Read,Write}Sectors numbers are in sectors/second.
91// A sector is usually 512 bytes.
92
93const char MetricsDaemon::kMetricReadSectorsLongName[] =
94 "Platform.ReadSectorsLong";
95const char MetricsDaemon::kMetricWriteSectorsLongName[] =
96 "Platform.WriteSectorsLong";
97const char MetricsDaemon::kMetricReadSectorsShortName[] =
98 "Platform.ReadSectorsShort";
99const char MetricsDaemon::kMetricWriteSectorsShortName[] =
100 "Platform.WriteSectorsShort";
101
102const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds
103const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds
104
105// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
106// sectors.
107const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
108const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
109
Ken Mixter4c5daa42010-08-26 18:35:06 -0700110// persistent metrics path
111const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700112
Darin Petkov1bb904e2010-06-16 15:58:06 -0700113
Darin Petkov703ec972010-04-27 11:02:18 -0700114// static
Darin Petkov41e06232010-05-03 16:45:37 -0700115const char* MetricsDaemon::kDBusMatches_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700116 "type='signal',"
Darin Petkov1bb904e2010-06-16 15:58:06 -0700117 "interface='" DBUS_IFACE_CRASH_REPORTER "',"
118 "path='/',"
119 "member='UserCrash'",
120
121 "type='signal',"
Darin Petkov703ec972010-04-27 11:02:18 -0700122 "interface='" DBUS_IFACE_POWER_MANAGER "',"
Benson Leung53faeb02010-06-08 15:59:13 -0700123 "path='/'",
Darin Petkov41e06232010-05-03 16:45:37 -0700124
125 "type='signal',"
126 "sender='org.chromium.SessionManager',"
127 "interface='" DBUS_IFACE_SESSION_MANAGER "',"
128 "path='/org/chromium/SessionManager',"
129 "member='SessionStateChanged'",
Darin Petkov703ec972010-04-27 11:02:18 -0700130};
131
132// static
Darin Petkov41e06232010-05-03 16:45:37 -0700133const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700134#define STATE(name, capname) #name,
135#include "power_states.h"
136};
137
Darin Petkov41e06232010-05-03 16:45:37 -0700138// static
Darin Petkov41e06232010-05-03 16:45:37 -0700139const char* MetricsDaemon::kSessionStates_[] = {
140#define STATE(name, capname) #name,
141#include "session_states.h"
142};
143
Darin Petkovf1e85e42010-06-10 15:59:53 -0700144MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700145 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700146 session_state_(kUnknownSessionState),
147 user_active_(false),
148 usemon_interval_(0),
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800149 usemon_source_(NULL),
150 diskstats_path_(NULL) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700151
Ken Mixter4c5daa42010-08-26 18:35:06 -0700152MetricsDaemon::~MetricsDaemon() {
153 DeleteFrequencyCounters();
154}
155
156void MetricsDaemon::DeleteFrequencyCounters() {
157 for (FrequencyCounters::iterator i = frequency_counters_.begin();
158 i != frequency_counters_.end(); ++i) {
159 delete i->second;
160 i->second = NULL;
161 }
162}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700163
Darin Petkov2ccef012010-05-05 16:06:37 -0700164void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700165 if (run_as_daemon && daemon(0, 0) != 0)
166 return;
167
Ken Mixterccd84c02010-08-16 19:57:13 -0700168 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
169 ProcessKernelCrash();
170 }
171
172 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
173 ProcessUncleanShutdown();
174 }
175
Darin Petkov38d5cb02010-06-24 12:10:26 -0700176 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700177}
178
Ken Mixter4c5daa42010-08-26 18:35:06 -0700179FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
180 return FilePath(kMetricsPath).Append(histogram_name);
181}
182
183void MetricsDaemon::ConfigureCrashIntervalReporter(
184 const char* histogram_name,
185 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
186 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
187 FilePath file_path = GetHistogramPath(histogram_name);
188 (*reporter)->Init(file_path.value().c_str(),
189 histogram_name,
190 kMetricCrashIntervalMin,
191 kMetricCrashIntervalMax,
192 kMetricCrashIntervalBuckets);
193}
194
195void MetricsDaemon::ConfigureCrashFrequencyReporter(
196 const char* histogram_name) {
197 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
198 new chromeos_metrics::TaggedCounterReporter());
199 FilePath file_path = GetHistogramPath(histogram_name);
200 reporter->Init(file_path.value().c_str(),
201 histogram_name,
202 kMetricCrashFrequencyMin,
203 kMetricCrashFrequencyMax,
204 kMetricCrashFrequencyBuckets);
205 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
206 new chromeos_metrics::FrequencyCounter());
207 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
208 chromeos_metrics::kSecondsPerWeek :
209 chromeos_metrics::kSecondsPerDay;
210 new_counter->Init(
211 static_cast<chromeos_metrics::TaggedCounterInterface*>(
212 reporter.release()),
213 cycle_duration);
214 frequency_counters_[histogram_name] = new_counter.release();
215}
216
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800217void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
218 const char* diskstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700219 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700220 DCHECK(metrics_lib != NULL);
221 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700222 chromeos_metrics::TaggedCounterReporter::
223 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700224
225 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700226 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700227 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700228
Ken Mixter4c5daa42010-08-26 18:35:06 -0700229 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
230 &kernel_crash_interval_);
231 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
232 &unclean_shutdown_interval_);
233 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
234 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700235
Ken Mixter4c5daa42010-08-26 18:35:06 -0700236 DeleteFrequencyCounters();
237 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700238 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
239 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
240 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
241 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
242 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
243 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
244 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700245
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800246 diskstats_path_ = diskstats_path;
247 DiskStatsReporterInit();
248
Darin Petkov2ccef012010-05-05 16:06:37 -0700249 // Don't setup D-Bus and GLib in test mode.
250 if (testing)
251 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700252
Darin Petkov703ec972010-04-27 11:02:18 -0700253 g_thread_init(NULL);
254 g_type_init();
255 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700256
Darin Petkov703ec972010-04-27 11:02:18 -0700257 DBusError error;
258 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700259
David James3b3add52010-06-04 15:01:19 -0700260 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700261 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
262 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700263
Darin Petkov703ec972010-04-27 11:02:18 -0700264 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700265
Darin Petkov703ec972010-04-27 11:02:18 -0700266 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700267 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700268 const char* match = kDBusMatches_[m];
269 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700270 dbus_bus_add_match(connection, match, &error);
271 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
272 "unable to add a match: " << SAFE_MESSAGE(error);
273 }
274
275 // Adds the D-Bus filter routine to be called back whenever one of
276 // the registered D-Bus matches is successful. The daemon is not
277 // activated for D-Bus messages that don't match.
278 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700279}
280
281void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700282 GMainLoop* loop = g_main_loop_new(NULL, false);
283 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700284}
285
Darin Petkov703ec972010-04-27 11:02:18 -0700286// static
287DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
288 DBusMessage* message,
289 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700290 Time now = Time::Now();
291 TimeTicks ticks = TimeTicks::Now();
292 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700293
294 int message_type = dbus_message_get_type(message);
295 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700296 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700297 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
298 }
299
300 // Signal messages always have interfaces.
301 const char* interface = dbus_message_get_interface(message);
302 CHECK(interface != NULL);
303
304 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
305
306 DBusMessageIter iter;
307 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700308 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
309 CHECK(strcmp(dbus_message_get_member(message),
310 "UserCrash") == 0);
311 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700312 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700313 const char* member = dbus_message_get_member(message);
314 if (strcmp(member, "ScreenIsLocked") == 0) {
315 daemon->SetUserActiveState(false, now);
316 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
317 daemon->SetUserActiveState(true, now);
318 } else if (strcmp(member, "PowerStateChanged") == 0) {
319 char* state_name;
320 dbus_message_iter_get_basic(&iter, &state_name);
321 daemon->PowerStateChanged(state_name, now);
322 }
Darin Petkov41e06232010-05-03 16:45:37 -0700323 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
324 CHECK(strcmp(dbus_message_get_member(message),
325 "SessionStateChanged") == 0);
326
David James3b3add52010-06-04 15:01:19 -0700327 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700328 dbus_message_iter_get_basic(&iter, &state_name);
329 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700330 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700331 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700332 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
333 }
334
335 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700336}
337
Darin Petkovf27f0362010-06-04 13:14:19 -0700338void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700339 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700340 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700341
342 if (power_state_ != kPowerStateOn)
343 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700344}
345
346MetricsDaemon::PowerState
347MetricsDaemon::LookupPowerState(const char* state_name) {
348 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700349 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700350 return static_cast<PowerState>(i);
351 }
352 }
Darin Petkov41e06232010-05-03 16:45:37 -0700353 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700354 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700355}
356
Darin Petkovf27f0362010-06-04 13:14:19 -0700357void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700358 DLOG(INFO) << "user session state: " << state_name;
359 session_state_ = LookupSessionState(state_name);
360 SetUserActiveState(session_state_ == kSessionStateStarted, now);
361}
362
363MetricsDaemon::SessionState
364MetricsDaemon::LookupSessionState(const char* state_name) {
365 for (int i = 0; i < kNumberSessionStates; i++) {
366 if (strcmp(state_name, kSessionStates_[i]) == 0) {
367 return static_cast<SessionState>(i);
368 }
369 }
370 DLOG(WARNING) << "unknown user session state: " << state_name;
371 return kUnknownSessionState;
372}
373
Darin Petkovf27f0362010-06-04 13:14:19 -0700374void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700375 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
376
377 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700378 // the day since Epoch, and logs the usage data. Guards against the
379 // time jumping back and forth due to the user changing it by
380 // discarding the new use time.
381 int seconds = 0;
382 if (user_active_ && now > user_active_last_) {
383 TimeDelta since_active = now - user_active_last_;
384 if (since_active < TimeDelta::FromSeconds(
385 kUseMonitorIntervalMax + kSecondsPerMinute)) {
386 seconds = static_cast<int>(since_active.InSeconds());
387 }
388 }
389 TimeDelta since_epoch = now - Time();
390 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700391 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700392 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700393 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700394
Ken Mixter4c5daa42010-08-26 18:35:06 -0700395 // Flush finished cycles of all frequency counters.
396 for (FrequencyCounters::iterator i = frequency_counters_.begin();
397 i != frequency_counters_.end(); ++i) {
398 i->second->FlushFinishedCycles();
399 }
400
Darin Petkov41e06232010-05-03 16:45:37 -0700401 // Schedules a use monitor on inactive->active transitions and
402 // unschedules it on active->inactive transitions.
403 if (!user_active_ && active)
404 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
405 else if (user_active_ && !active)
406 UnscheduleUseMonitor();
407
408 // Remembers the current active state and the time of the last
409 // activity update.
410 user_active_ = active;
411 user_active_last_ = now;
412}
413
Darin Petkov1bb904e2010-06-16 15:58:06 -0700414void MetricsDaemon::ProcessUserCrash() {
415 // Counts the active use time up to now.
416 SetUserActiveState(user_active_, Time::Now());
417
418 // Reports the active use time since the last crash and resets it.
419 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700420
Ken Mixter4c5daa42010-08-26 18:35:06 -0700421 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
422 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
423 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
424 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700425}
426
Darin Petkov38d5cb02010-06-24 12:10:26 -0700427void MetricsDaemon::ProcessKernelCrash() {
428 // Counts the active use time up to now.
429 SetUserActiveState(user_active_, Time::Now());
430
431 // Reports the active use time since the last crash and resets it.
432 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700433
Ken Mixter4c5daa42010-08-26 18:35:06 -0700434 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
435 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
436 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
437 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700438}
439
Ken Mixterccd84c02010-08-16 19:57:13 -0700440void MetricsDaemon::ProcessUncleanShutdown() {
441 // Counts the active use time up to now.
442 SetUserActiveState(user_active_, Time::Now());
443
444 // Reports the active use time since the last crash and resets it.
445 unclean_shutdown_interval_->Flush();
446
Ken Mixter4c5daa42010-08-26 18:35:06 -0700447 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
448 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
449 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
450 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700451}
452
453bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700454 FilePath crash_detected(crash_file);
455 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700456 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700457
458 // Deletes the crash-detected file so that the daemon doesn't report
459 // another kernel crash in case it's restarted.
460 file_util::Delete(crash_detected,
461 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700462 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700463}
464
Darin Petkov41e06232010-05-03 16:45:37 -0700465// static
466gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
467 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
468}
469
470bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700471 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700472
473 // If a new monitor source/instance is scheduled, returns false to
474 // tell GLib to destroy this monitor source/instance. Returns true
475 // otherwise to keep calling back this monitor.
476 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
477}
478
479bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
480{
Darin Petkov2ccef012010-05-05 16:06:37 -0700481 if (testing_)
482 return false;
483
Darin Petkov41e06232010-05-03 16:45:37 -0700484 // Caps the interval -- the bigger the interval, the more active use
485 // time will be potentially dropped on system shutdown.
486 if (interval > kUseMonitorIntervalMax)
487 interval = kUseMonitorIntervalMax;
488
489 if (backoff) {
490 // Back-off mode is used by the use monitor to reschedule itself
491 // with exponential back-off in time. This mode doesn't create a
492 // new timeout source if the new interval is the same as the old
493 // one. Also, if a new timeout source is created, the old one is
494 // not destroyed explicitly here -- it will be destroyed by GLib
495 // when the monitor returns FALSE (see UseMonitor and
496 // UseMonitorStatic).
497 if (interval == usemon_interval_)
498 return false;
499 } else {
500 UnscheduleUseMonitor();
501 }
502
503 // Schedules a new use monitor for |interval| seconds from now.
504 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
505 usemon_source_ = g_timeout_source_new_seconds(interval);
506 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
507 NULL); // No destroy notification.
508 g_source_attach(usemon_source_,
509 NULL); // Default context.
510 usemon_interval_ = interval;
511 return true;
512}
513
514void MetricsDaemon::UnscheduleUseMonitor() {
515 // If there's a use monitor scheduled already, destroys it.
516 if (usemon_source_ == NULL)
517 return;
518
519 DLOG(INFO) << "destroying use monitor";
520 g_source_destroy(usemon_source_);
521 usemon_source_ = NULL;
522 usemon_interval_ = 0;
523}
524
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800525void MetricsDaemon::DiskStatsReporterInit() {
526 DiskStatsReadStats(&read_sectors_, &write_sectors_);
527 // The first time around just run the long stat, so we don't delay boot.
528 diskstats_state_ = kDiskStatsLong;
529 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval);
530}
531
532void MetricsDaemon::ScheduleDiskStatsCallback(int wait) {
533 if (testing_) {
534 return;
535 }
536 g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this);
537}
538
539void MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
540 long int* write_sectors) {
541 int nchars;
542 int nitems;
543 char line[200];
544 int file = HANDLE_EINTR(open(diskstats_path_, O_RDONLY));
545 if (file < 0) {
546 PLOG(WARNING) << "cannot open " << diskstats_path_;
547 return;
548 }
549 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
550 if (nchars < 0) {
551 PLOG(WARNING) << "cannot read from " << diskstats_path_;
552 } else {
553 LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in "
554 << diskstats_path_;
555 line[nchars] = '\0';
556 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
557 read_sectors, write_sectors);
558 LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in "
559 << diskstats_path_ << ", expected 2";
560 }
561 HANDLE_EINTR(close(file));
562}
563
564// static
565gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) {
566 (static_cast<MetricsDaemon*>(handle))->DiskStatsCallback();
567 return false; // one-time callback
568}
569
570void MetricsDaemon::DiskStatsCallback() {
571 long int read_sectors_now, write_sectors_now;
572 DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
573
574 switch (diskstats_state_) {
575 case kDiskStatsShort:
576 SendMetric(kMetricReadSectorsShortName,
577 (int) (read_sectors_now - read_sectors_) /
578 kMetricDiskStatsShortInterval,
579 1,
580 kMetricSectorsIOMax,
581 kMetricSectorsBuckets);
582 SendMetric(kMetricWriteSectorsShortName,
583 (int) (write_sectors_now - write_sectors_) /
584 kMetricDiskStatsShortInterval,
585 1,
586 kMetricSectorsIOMax,
587 kMetricSectorsBuckets);
588 // Schedule long callback.
589 diskstats_state_ = kDiskStatsLong;
590 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval -
591 kMetricDiskStatsShortInterval);
592 break;
593 case kDiskStatsLong:
594 SendMetric(kMetricReadSectorsLongName,
595 (int) (read_sectors_now - read_sectors_) /
596 kMetricDiskStatsLongInterval,
597 1,
598 kMetricSectorsIOMax,
599 kMetricSectorsBuckets);
600 SendMetric(kMetricWriteSectorsLongName,
601 (int) (write_sectors_now - write_sectors_) /
602 kMetricDiskStatsLongInterval,
603 1,
604 kMetricSectorsIOMax,
605 kMetricSectorsBuckets);
606 // Reset sector counters
607 read_sectors_ = read_sectors_now;
608 write_sectors_ = write_sectors_now;
609 // Schedule short callback.
610 diskstats_state_ = kDiskStatsShort;
611 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval);
612 break;
613 default:
614 LOG(FATAL) << "Invalid disk stats state";
615 }
616}
617
Darin Petkovf1e85e42010-06-10 15:59:53 -0700618// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700619void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700620 if (count <= 0)
621 return;
622
Darin Petkovf1e85e42010-06-10 15:59:53 -0700623 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
624 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
625 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
626 kMetricDailyUseTimeMin,
627 kMetricDailyUseTimeMax,
628 kMetricDailyUseTimeBuckets);
629}
630
Darin Petkov38d5cb02010-06-24 12:10:26 -0700631void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700632 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700633 DLOG(INFO) << "received metric: " << name << " " << sample << " "
634 << min << " " << max << " " << nbuckets;
635 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700636}