blob: 4820590d1b035af0355d1d19701309df8d35bfe4 [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 Semenzato0f132bb2011-02-28 11:17:43 -0800149 usemon_source_(NULL) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700150
Ken Mixter4c5daa42010-08-26 18:35:06 -0700151MetricsDaemon::~MetricsDaemon() {
152 DeleteFrequencyCounters();
153}
154
155void MetricsDaemon::DeleteFrequencyCounters() {
156 for (FrequencyCounters::iterator i = frequency_counters_.begin();
157 i != frequency_counters_.end(); ++i) {
158 delete i->second;
159 i->second = NULL;
160 }
161}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700162
Darin Petkov2ccef012010-05-05 16:06:37 -0700163void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700164 if (run_as_daemon && daemon(0, 0) != 0)
165 return;
166
Ken Mixterccd84c02010-08-16 19:57:13 -0700167 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
168 ProcessKernelCrash();
169 }
170
171 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
172 ProcessUncleanShutdown();
173 }
174
Darin Petkov38d5cb02010-06-24 12:10:26 -0700175 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700176}
177
Ken Mixter4c5daa42010-08-26 18:35:06 -0700178FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
179 return FilePath(kMetricsPath).Append(histogram_name);
180}
181
182void MetricsDaemon::ConfigureCrashIntervalReporter(
183 const char* histogram_name,
184 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
185 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
186 FilePath file_path = GetHistogramPath(histogram_name);
187 (*reporter)->Init(file_path.value().c_str(),
188 histogram_name,
189 kMetricCrashIntervalMin,
190 kMetricCrashIntervalMax,
191 kMetricCrashIntervalBuckets);
192}
193
194void MetricsDaemon::ConfigureCrashFrequencyReporter(
195 const char* histogram_name) {
196 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
197 new chromeos_metrics::TaggedCounterReporter());
198 FilePath file_path = GetHistogramPath(histogram_name);
199 reporter->Init(file_path.value().c_str(),
200 histogram_name,
201 kMetricCrashFrequencyMin,
202 kMetricCrashFrequencyMax,
203 kMetricCrashFrequencyBuckets);
204 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
205 new chromeos_metrics::FrequencyCounter());
206 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
207 chromeos_metrics::kSecondsPerWeek :
208 chromeos_metrics::kSecondsPerDay;
209 new_counter->Init(
210 static_cast<chromeos_metrics::TaggedCounterInterface*>(
211 reporter.release()),
212 cycle_duration);
213 frequency_counters_[histogram_name] = new_counter.release();
214}
215
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800216void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800217 string diskstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700218 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700219 DCHECK(metrics_lib != NULL);
220 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700221 chromeos_metrics::TaggedCounterReporter::
222 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700223
224 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700225 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700226 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700227
Ken Mixter4c5daa42010-08-26 18:35:06 -0700228 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
229 &kernel_crash_interval_);
230 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
231 &unclean_shutdown_interval_);
232 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
233 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700234
Ken Mixter4c5daa42010-08-26 18:35:06 -0700235 DeleteFrequencyCounters();
236 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700237 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
238 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
239 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
240 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
241 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
242 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
243 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700244
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800245 // Don't attempt to collect disk stats if there is no disk stats file.
246 if (!diskstats_path.empty()) {
247 diskstats_path_ = diskstats_path;
248 DiskStatsReporterInit();
249 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800250
Darin Petkov2ccef012010-05-05 16:06:37 -0700251 // Don't setup D-Bus and GLib in test mode.
252 if (testing)
253 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700254
Darin Petkov703ec972010-04-27 11:02:18 -0700255 g_thread_init(NULL);
256 g_type_init();
257 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700258
Darin Petkov703ec972010-04-27 11:02:18 -0700259 DBusError error;
260 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700261
David James3b3add52010-06-04 15:01:19 -0700262 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700263 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
264 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700265
Darin Petkov703ec972010-04-27 11:02:18 -0700266 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700267
Darin Petkov703ec972010-04-27 11:02:18 -0700268 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700269 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700270 const char* match = kDBusMatches_[m];
271 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700272 dbus_bus_add_match(connection, match, &error);
273 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
274 "unable to add a match: " << SAFE_MESSAGE(error);
275 }
276
277 // Adds the D-Bus filter routine to be called back whenever one of
278 // the registered D-Bus matches is successful. The daemon is not
279 // activated for D-Bus messages that don't match.
280 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700281}
282
283void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700284 GMainLoop* loop = g_main_loop_new(NULL, false);
285 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700286}
287
Darin Petkov703ec972010-04-27 11:02:18 -0700288// static
289DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
290 DBusMessage* message,
291 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700292 Time now = Time::Now();
293 TimeTicks ticks = TimeTicks::Now();
294 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700295
296 int message_type = dbus_message_get_type(message);
297 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700298 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700299 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
300 }
301
302 // Signal messages always have interfaces.
303 const char* interface = dbus_message_get_interface(message);
304 CHECK(interface != NULL);
305
306 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
307
308 DBusMessageIter iter;
309 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700310 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
311 CHECK(strcmp(dbus_message_get_member(message),
312 "UserCrash") == 0);
313 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700314 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700315 const char* member = dbus_message_get_member(message);
316 if (strcmp(member, "ScreenIsLocked") == 0) {
317 daemon->SetUserActiveState(false, now);
318 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
319 daemon->SetUserActiveState(true, now);
320 } else if (strcmp(member, "PowerStateChanged") == 0) {
321 char* state_name;
322 dbus_message_iter_get_basic(&iter, &state_name);
323 daemon->PowerStateChanged(state_name, now);
324 }
Darin Petkov41e06232010-05-03 16:45:37 -0700325 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
326 CHECK(strcmp(dbus_message_get_member(message),
327 "SessionStateChanged") == 0);
328
David James3b3add52010-06-04 15:01:19 -0700329 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700330 dbus_message_iter_get_basic(&iter, &state_name);
331 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700332 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700333 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700334 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
335 }
336
337 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700338}
339
Darin Petkovf27f0362010-06-04 13:14:19 -0700340void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700341 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700342 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700343
344 if (power_state_ != kPowerStateOn)
345 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700346}
347
348MetricsDaemon::PowerState
349MetricsDaemon::LookupPowerState(const char* state_name) {
350 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700351 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700352 return static_cast<PowerState>(i);
353 }
354 }
Darin Petkov41e06232010-05-03 16:45:37 -0700355 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700356 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700357}
358
Darin Petkovf27f0362010-06-04 13:14:19 -0700359void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700360 DLOG(INFO) << "user session state: " << state_name;
361 session_state_ = LookupSessionState(state_name);
362 SetUserActiveState(session_state_ == kSessionStateStarted, now);
363}
364
365MetricsDaemon::SessionState
366MetricsDaemon::LookupSessionState(const char* state_name) {
367 for (int i = 0; i < kNumberSessionStates; i++) {
368 if (strcmp(state_name, kSessionStates_[i]) == 0) {
369 return static_cast<SessionState>(i);
370 }
371 }
372 DLOG(WARNING) << "unknown user session state: " << state_name;
373 return kUnknownSessionState;
374}
375
Darin Petkovf27f0362010-06-04 13:14:19 -0700376void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700377 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
378
379 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700380 // the day since Epoch, and logs the usage data. Guards against the
381 // time jumping back and forth due to the user changing it by
382 // discarding the new use time.
383 int seconds = 0;
384 if (user_active_ && now > user_active_last_) {
385 TimeDelta since_active = now - user_active_last_;
386 if (since_active < TimeDelta::FromSeconds(
387 kUseMonitorIntervalMax + kSecondsPerMinute)) {
388 seconds = static_cast<int>(since_active.InSeconds());
389 }
390 }
391 TimeDelta since_epoch = now - Time();
392 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700393 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700394 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700395 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700396
Ken Mixter4c5daa42010-08-26 18:35:06 -0700397 // Flush finished cycles of all frequency counters.
398 for (FrequencyCounters::iterator i = frequency_counters_.begin();
399 i != frequency_counters_.end(); ++i) {
400 i->second->FlushFinishedCycles();
401 }
402
Darin Petkov41e06232010-05-03 16:45:37 -0700403 // Schedules a use monitor on inactive->active transitions and
404 // unschedules it on active->inactive transitions.
405 if (!user_active_ && active)
406 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
407 else if (user_active_ && !active)
408 UnscheduleUseMonitor();
409
410 // Remembers the current active state and the time of the last
411 // activity update.
412 user_active_ = active;
413 user_active_last_ = now;
414}
415
Darin Petkov1bb904e2010-06-16 15:58:06 -0700416void MetricsDaemon::ProcessUserCrash() {
417 // Counts the active use time up to now.
418 SetUserActiveState(user_active_, Time::Now());
419
420 // Reports the active use time since the last crash and resets it.
421 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700422
Ken Mixter4c5daa42010-08-26 18:35:06 -0700423 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
424 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
425 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
426 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700427}
428
Darin Petkov38d5cb02010-06-24 12:10:26 -0700429void MetricsDaemon::ProcessKernelCrash() {
430 // Counts the active use time up to now.
431 SetUserActiveState(user_active_, Time::Now());
432
433 // Reports the active use time since the last crash and resets it.
434 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700435
Ken Mixter4c5daa42010-08-26 18:35:06 -0700436 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
437 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
438 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
439 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700440}
441
Ken Mixterccd84c02010-08-16 19:57:13 -0700442void MetricsDaemon::ProcessUncleanShutdown() {
443 // Counts the active use time up to now.
444 SetUserActiveState(user_active_, Time::Now());
445
446 // Reports the active use time since the last crash and resets it.
447 unclean_shutdown_interval_->Flush();
448
Ken Mixter4c5daa42010-08-26 18:35:06 -0700449 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
450 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
451 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
452 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700453}
454
455bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700456 FilePath crash_detected(crash_file);
457 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700458 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700459
460 // Deletes the crash-detected file so that the daemon doesn't report
461 // another kernel crash in case it's restarted.
462 file_util::Delete(crash_detected,
463 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700464 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700465}
466
Darin Petkov41e06232010-05-03 16:45:37 -0700467// static
468gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
469 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
470}
471
472bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700473 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700474
475 // If a new monitor source/instance is scheduled, returns false to
476 // tell GLib to destroy this monitor source/instance. Returns true
477 // otherwise to keep calling back this monitor.
478 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
479}
480
481bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
482{
Darin Petkov2ccef012010-05-05 16:06:37 -0700483 if (testing_)
484 return false;
485
Darin Petkov41e06232010-05-03 16:45:37 -0700486 // Caps the interval -- the bigger the interval, the more active use
487 // time will be potentially dropped on system shutdown.
488 if (interval > kUseMonitorIntervalMax)
489 interval = kUseMonitorIntervalMax;
490
491 if (backoff) {
492 // Back-off mode is used by the use monitor to reschedule itself
493 // with exponential back-off in time. This mode doesn't create a
494 // new timeout source if the new interval is the same as the old
495 // one. Also, if a new timeout source is created, the old one is
496 // not destroyed explicitly here -- it will be destroyed by GLib
497 // when the monitor returns FALSE (see UseMonitor and
498 // UseMonitorStatic).
499 if (interval == usemon_interval_)
500 return false;
501 } else {
502 UnscheduleUseMonitor();
503 }
504
505 // Schedules a new use monitor for |interval| seconds from now.
506 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
507 usemon_source_ = g_timeout_source_new_seconds(interval);
508 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
509 NULL); // No destroy notification.
510 g_source_attach(usemon_source_,
511 NULL); // Default context.
512 usemon_interval_ = interval;
513 return true;
514}
515
516void MetricsDaemon::UnscheduleUseMonitor() {
517 // If there's a use monitor scheduled already, destroys it.
518 if (usemon_source_ == NULL)
519 return;
520
521 DLOG(INFO) << "destroying use monitor";
522 g_source_destroy(usemon_source_);
523 usemon_source_ = NULL;
524 usemon_interval_ = 0;
525}
526
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800527void MetricsDaemon::DiskStatsReporterInit() {
528 DiskStatsReadStats(&read_sectors_, &write_sectors_);
529 // The first time around just run the long stat, so we don't delay boot.
530 diskstats_state_ = kDiskStatsLong;
531 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval);
532}
533
534void MetricsDaemon::ScheduleDiskStatsCallback(int wait) {
535 if (testing_) {
536 return;
537 }
538 g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this);
539}
540
541void MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
542 long int* write_sectors) {
543 int nchars;
544 int nitems;
545 char line[200];
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800546 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800547 if (file < 0) {
548 PLOG(WARNING) << "cannot open " << diskstats_path_;
549 return;
550 }
551 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
552 if (nchars < 0) {
553 PLOG(WARNING) << "cannot read from " << diskstats_path_;
554 } else {
555 LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in "
556 << diskstats_path_;
557 line[nchars] = '\0';
558 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
559 read_sectors, write_sectors);
560 LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in "
561 << diskstats_path_ << ", expected 2";
562 }
563 HANDLE_EINTR(close(file));
564}
565
566// static
567gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) {
568 (static_cast<MetricsDaemon*>(handle))->DiskStatsCallback();
569 return false; // one-time callback
570}
571
572void MetricsDaemon::DiskStatsCallback() {
573 long int read_sectors_now, write_sectors_now;
574 DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
575
576 switch (diskstats_state_) {
577 case kDiskStatsShort:
578 SendMetric(kMetricReadSectorsShortName,
579 (int) (read_sectors_now - read_sectors_) /
580 kMetricDiskStatsShortInterval,
581 1,
582 kMetricSectorsIOMax,
583 kMetricSectorsBuckets);
584 SendMetric(kMetricWriteSectorsShortName,
585 (int) (write_sectors_now - write_sectors_) /
586 kMetricDiskStatsShortInterval,
587 1,
588 kMetricSectorsIOMax,
589 kMetricSectorsBuckets);
590 // Schedule long callback.
591 diskstats_state_ = kDiskStatsLong;
592 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval -
593 kMetricDiskStatsShortInterval);
594 break;
595 case kDiskStatsLong:
596 SendMetric(kMetricReadSectorsLongName,
597 (int) (read_sectors_now - read_sectors_) /
598 kMetricDiskStatsLongInterval,
599 1,
600 kMetricSectorsIOMax,
601 kMetricSectorsBuckets);
602 SendMetric(kMetricWriteSectorsLongName,
603 (int) (write_sectors_now - write_sectors_) /
604 kMetricDiskStatsLongInterval,
605 1,
606 kMetricSectorsIOMax,
607 kMetricSectorsBuckets);
608 // Reset sector counters
609 read_sectors_ = read_sectors_now;
610 write_sectors_ = write_sectors_now;
611 // Schedule short callback.
612 diskstats_state_ = kDiskStatsShort;
613 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval);
614 break;
615 default:
616 LOG(FATAL) << "Invalid disk stats state";
617 }
618}
619
Darin Petkovf1e85e42010-06-10 15:59:53 -0700620// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700621void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700622 if (count <= 0)
623 return;
624
Darin Petkovf1e85e42010-06-10 15:59:53 -0700625 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
626 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
627 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
628 kMetricDailyUseTimeMin,
629 kMetricDailyUseTimeMax,
630 kMetricDailyUseTimeBuckets);
631}
632
Darin Petkov38d5cb02010-06-24 12:10:26 -0700633void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700634 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700635 DLOG(INFO) << "received metric: " << name << " " << sample << " "
636 << min << " " << max << " " << nbuckets;
637 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700638}