blob: 0171fdec952fa650587d60e3b9b9f55f988e28c6 [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
Darin Petkov703ec972010-04-27 11:02:18 -07007#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -07008
Darin Petkov38d5cb02010-06-24 12:10:26 -07009#include <base/file_util.h>
Darin Petkov65b01462010-04-14 13:32:20 -070010#include <base/logging.h>
11
Darin Petkovf1e85e42010-06-10 15:59:53 -070012#include "counter.h"
13
Darin Petkovf27f0362010-06-04 13:14:19 -070014using base::Time;
15using base::TimeDelta;
16using base::TimeTicks;
Darin Petkov38d5cb02010-06-24 12:10:26 -070017using std::string;
Darin Petkovf27f0362010-06-04 13:14:19 -070018
Darin Petkov703ec972010-04-27 11:02:18 -070019#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov1bb904e2010-06-16 15:58:06 -070020#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter"
Darin Petkove3348402010-06-04 14:07:41 -070021#define DBUS_IFACE_FLIMFLAM_MANAGER "org.chromium.flimflam.Manager"
David James6bf6e252010-06-06 18:52:40 -070022#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager"
Darin Petkov41e06232010-05-03 16:45:37 -070023#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface"
24
Darin Petkov41e06232010-05-03 16:45:37 -070025static const int kSecondsPerMinute = 60;
26static const int kMinutesPerHour = 60;
27static const int kHoursPerDay = 24;
28static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070029static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
30static const int kDaysPerWeek = 7;
31static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070032
33// The daily use monitor is scheduled to a 1-minute interval after
34// initial user activity and then it's exponentially backed off to
35// 10-minute intervals. Although not required, the back off is
36// implemented because the histogram buckets are spaced exponentially
37// anyway and to avoid too frequent metrics daemon process wake-ups
38// and file I/O.
39static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
40static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070041
Ken Mixterccd84c02010-08-16 19:57:13 -070042const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
43static const char kUncleanShutdownDetectedFile[] =
44 "/tmp/unclean-shutdown-detected";
45
Darin Petkov2ccef012010-05-05 16:06:37 -070046// static metrics parameters.
47const char MetricsDaemon::kMetricDailyUseTimeName[] =
48 "Logging.DailyUseTime";
49const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
50const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
51const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
52
53const char MetricsDaemon::kMetricTimeToNetworkDropName[] =
54 "Network.TimeToDrop";
55const int MetricsDaemon::kMetricTimeToNetworkDropMin = 1;
56const int MetricsDaemon::kMetricTimeToNetworkDropMax =
57 8 /* hours */ * kMinutesPerHour * kSecondsPerMinute;
58const int MetricsDaemon::kMetricTimeToNetworkDropBuckets = 50;
59
Ken Mixterccd84c02010-08-16 19:57:13 -070060// crash interval metrics
61const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
62 "Logging.KernelCrashInterval";
63const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
64 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070065const char MetricsDaemon::kMetricUserCrashIntervalName[] =
66 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070067
68const int MetricsDaemon::kMetricCrashIntervalMin = 1;
69const int MetricsDaemon::kMetricCrashIntervalMax =
70 4 * kSecondsPerWeek;
71const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
72
73// crash frequency metrics
74const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
75 "Logging.AnyCrashesDaily";
76const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
77 "Logging.KernelCrashesDaily";
78const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
79 "Logging.UncleanShutdownsDaily";
80const char MetricsDaemon::kMetricUserCrashesDailyName[] =
81 "Logging.UserCrashesDaily";
82const char MetricsDaemon::kMetricCrashesDailyMin = 1;
83const char MetricsDaemon::kMetricCrashesDailyMax = 100;
84const char MetricsDaemon::kMetricCrashesDailyBuckets = 50;
85
86
Darin Petkov1bb904e2010-06-16 15:58:06 -070087
Darin Petkov703ec972010-04-27 11:02:18 -070088// static
Darin Petkov41e06232010-05-03 16:45:37 -070089const char* MetricsDaemon::kDBusMatches_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -070090 "type='signal',"
Darin Petkov1bb904e2010-06-16 15:58:06 -070091 "interface='" DBUS_IFACE_CRASH_REPORTER "',"
92 "path='/',"
93 "member='UserCrash'",
94
95 "type='signal',"
Darin Petkove3348402010-06-04 14:07:41 -070096 "sender='org.chromium.flimflam',"
97 "interface='" DBUS_IFACE_FLIMFLAM_MANAGER "',"
Darin Petkov703ec972010-04-27 11:02:18 -070098 "path='/',"
99 "member='StateChanged'",
100
101 "type='signal',"
102 "interface='" DBUS_IFACE_POWER_MANAGER "',"
Benson Leung53faeb02010-06-08 15:59:13 -0700103 "path='/'",
Darin Petkov41e06232010-05-03 16:45:37 -0700104
105 "type='signal',"
106 "sender='org.chromium.SessionManager',"
107 "interface='" DBUS_IFACE_SESSION_MANAGER "',"
108 "path='/org/chromium/SessionManager',"
109 "member='SessionStateChanged'",
Darin Petkov703ec972010-04-27 11:02:18 -0700110};
111
112// static
Darin Petkov41e06232010-05-03 16:45:37 -0700113const char* MetricsDaemon::kNetworkStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700114#define STATE(name, capname) #name,
Darin Petkov65b01462010-04-14 13:32:20 -0700115#include "network_states.h"
116};
117
Darin Petkov703ec972010-04-27 11:02:18 -0700118// static
Darin Petkov41e06232010-05-03 16:45:37 -0700119const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700120#define STATE(name, capname) #name,
121#include "power_states.h"
122};
123
Darin Petkov41e06232010-05-03 16:45:37 -0700124// static
Darin Petkov41e06232010-05-03 16:45:37 -0700125const char* MetricsDaemon::kSessionStates_[] = {
126#define STATE(name, capname) #name,
127#include "session_states.h"
128};
129
Darin Petkov38d5cb02010-06-24 12:10:26 -0700130// Invokes a remote method over D-Bus that takes no input arguments
131// and returns a string result. The method call is issued with a 2
132// second blocking timeout. Returns an empty string on failure or
133// timeout.
134static string DBusGetString(DBusConnection* connection,
135 const string& destination,
136 const string& path,
137 const string& interface,
138 const string& method) {
139 DBusMessage* message =
140 dbus_message_new_method_call(destination.c_str(),
141 path.c_str(),
142 interface.c_str(),
143 method.c_str());
144 if (!message) {
145 DLOG(WARNING) << "DBusGetString: unable to allocate a message";
146 return "";
147 }
148
149 DBusError error;
150 dbus_error_init(&error);
151 const int kTimeout = 2000; // ms
152 DLOG(INFO) << "DBusGetString: dest=" << destination << " path=" << path
153 << " iface=" << interface << " method=" << method;
154 DBusMessage* reply =
155 dbus_connection_send_with_reply_and_block(connection, message, kTimeout,
156 &error);
157 dbus_message_unref(message);
158 if (dbus_error_is_set(&error) || !reply) {
159 DLOG(WARNING) << "DBusGetString: call failed";
160 return "";
161 }
162 DBusMessageIter iter;
163 dbus_message_iter_init(reply, &iter);
164 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
165 NOTREACHED();
166 dbus_message_unref(reply);
167 return "";
168 }
169 const char* c_result = "";
170 dbus_message_iter_get_basic(&iter, &c_result);
171 string result = c_result;
172 DLOG(INFO) << "DBusGetString: result=" << result;
173 dbus_message_unref(reply);
174 return result;
175}
176
Darin Petkovf1e85e42010-06-10 15:59:53 -0700177MetricsDaemon::MetricsDaemon()
178 : network_state_(kUnknownNetworkState),
179 power_state_(kUnknownPowerState),
180 session_state_(kUnknownSessionState),
181 user_active_(false),
182 usemon_interval_(0),
183 usemon_source_(NULL) {}
184
185MetricsDaemon::~MetricsDaemon() {}
186
Darin Petkov2ccef012010-05-05 16:06:37 -0700187void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700188 if (run_as_daemon && daemon(0, 0) != 0)
189 return;
190
Ken Mixterccd84c02010-08-16 19:57:13 -0700191 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
192 ProcessKernelCrash();
193 }
194
195 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
196 ProcessUncleanShutdown();
197 }
198
Darin Petkov38d5cb02010-06-24 12:10:26 -0700199 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700200}
201
Darin Petkovfc91b422010-05-12 13:05:45 -0700202void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) {
Darin Petkov65b01462010-04-14 13:32:20 -0700203 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700204 DCHECK(metrics_lib != NULL);
205 metrics_lib_ = metrics_lib;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700206
207 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700208 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700209 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700210
211 static const char kUserCrashIntervalRecordFile[] =
212 "/var/log/metrics/user-crash-interval";
Darin Petkov1bb904e2010-06-16 15:58:06 -0700213 user_crash_interval_.reset(new chromeos_metrics::TaggedCounter());
214 user_crash_interval_->Init(kUserCrashIntervalRecordFile,
Ken Mixterccd84c02010-08-16 19:57:13 -0700215 &ReportUserCrashInterval, this);
Darin Petkov2ccef012010-05-05 16:06:37 -0700216
Darin Petkov38d5cb02010-06-24 12:10:26 -0700217 static const char kKernelCrashIntervalRecordFile[] =
218 "/var/log/metrics/kernel-crash-interval";
219 kernel_crash_interval_.reset(new chromeos_metrics::TaggedCounter());
220 kernel_crash_interval_->Init(kKernelCrashIntervalRecordFile,
Ken Mixterccd84c02010-08-16 19:57:13 -0700221 &ReportKernelCrashInterval, this);
222
223 static const char kUncleanShutdownDetectedFile[] =
224 "/var/log/metrics/unclean-shutdown-interval";
225 unclean_shutdown_interval_.reset(new chromeos_metrics::TaggedCounter());
226 unclean_shutdown_interval_->Init(kUncleanShutdownDetectedFile,
227 &ReportUncleanShutdownInterval, this);
228
229 static const char kUserCrashesDailyRecordFile[] =
230 "/var/log/metrics/user-crashes-daily";
231 user_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
232 user_crashes_daily_->Init(kUserCrashesDailyRecordFile,
233 &ReportUserCrashesDaily,
234 this,
235 chromeos_metrics::kSecondsPerDay);
236
237 static const char kKernelCrashesDailyRecordFile[] =
238 "/var/log/metrics/kernel-crashes-daily";
239 kernel_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
240 kernel_crashes_daily_->Init(kKernelCrashesDailyRecordFile,
241 &ReportKernelCrashesDaily,
242 this,
243 chromeos_metrics::kSecondsPerDay);
244
245 static const char kUncleanShutdownsDailyRecordFile[] =
246 "/var/log/metrics/unclean-shutdowns-daily";
247 unclean_shutdowns_daily_.reset(new chromeos_metrics::FrequencyCounter());
248 unclean_shutdowns_daily_->Init(kUncleanShutdownsDailyRecordFile,
249 &ReportUncleanShutdownsDaily,
250 this,
251 chromeos_metrics::kSecondsPerDay);
252
253 static const char kAnyCrashesUserCrashDailyRecordFile[] =
254 "/var/log/metrics/any-crashes-daily";
255 any_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter());
256 any_crashes_daily_->Init(kAnyCrashesUserCrashDailyRecordFile,
257 &ReportAnyCrashesDaily,
258 this,
259 chromeos_metrics::kSecondsPerDay);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700260
Darin Petkov2ccef012010-05-05 16:06:37 -0700261 // Don't setup D-Bus and GLib in test mode.
262 if (testing)
263 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700264
Darin Petkov703ec972010-04-27 11:02:18 -0700265 g_thread_init(NULL);
266 g_type_init();
267 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700268
Darin Petkov703ec972010-04-27 11:02:18 -0700269 DBusError error;
270 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700271
David James3b3add52010-06-04 15:01:19 -0700272 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700273 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
274 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700275
Darin Petkov703ec972010-04-27 11:02:18 -0700276 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700277
Darin Petkov703ec972010-04-27 11:02:18 -0700278 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700279 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700280 const char* match = kDBusMatches_[m];
281 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700282 dbus_bus_add_match(connection, match, &error);
283 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
284 "unable to add a match: " << SAFE_MESSAGE(error);
285 }
286
287 // Adds the D-Bus filter routine to be called back whenever one of
288 // the registered D-Bus matches is successful. The daemon is not
289 // activated for D-Bus messages that don't match.
290 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov38d5cb02010-06-24 12:10:26 -0700291
292 // Initializes the current network state by retrieving it from flimflam.
293 string state_name = DBusGetString(connection, "org.chromium.flimflam", "/",
294 DBUS_IFACE_FLIMFLAM_MANAGER, "GetState");
295 NetStateChanged(state_name.c_str(), TimeTicks::Now());
Darin Petkov65b01462010-04-14 13:32:20 -0700296}
297
298void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700299 GMainLoop* loop = g_main_loop_new(NULL, false);
300 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700301}
302
Darin Petkov703ec972010-04-27 11:02:18 -0700303// static
304DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
305 DBusMessage* message,
306 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700307 Time now = Time::Now();
308 TimeTicks ticks = TimeTicks::Now();
309 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700310
311 int message_type = dbus_message_get_type(message);
312 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700313 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700314 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
315 }
316
317 // Signal messages always have interfaces.
318 const char* interface = dbus_message_get_interface(message);
319 CHECK(interface != NULL);
320
321 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
322
323 DBusMessageIter iter;
324 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700325 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
326 CHECK(strcmp(dbus_message_get_member(message),
327 "UserCrash") == 0);
328 daemon->ProcessUserCrash();
329 } else if (strcmp(interface, DBUS_IFACE_FLIMFLAM_MANAGER) == 0) {
Darin Petkov41e06232010-05-03 16:45:37 -0700330 CHECK(strcmp(dbus_message_get_member(message),
331 "StateChanged") == 0);
Darin Petkov703ec972010-04-27 11:02:18 -0700332
David James3b3add52010-06-04 15:01:19 -0700333 char* state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700334 dbus_message_iter_get_basic(&iter, &state_name);
Darin Petkovf27f0362010-06-04 13:14:19 -0700335 daemon->NetStateChanged(state_name, ticks);
Darin Petkov703ec972010-04-27 11:02:18 -0700336 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700337 const char* member = dbus_message_get_member(message);
338 if (strcmp(member, "ScreenIsLocked") == 0) {
339 daemon->SetUserActiveState(false, now);
340 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
341 daemon->SetUserActiveState(true, now);
342 } else if (strcmp(member, "PowerStateChanged") == 0) {
343 char* state_name;
344 dbus_message_iter_get_basic(&iter, &state_name);
345 daemon->PowerStateChanged(state_name, now);
346 }
Darin Petkov41e06232010-05-03 16:45:37 -0700347 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
348 CHECK(strcmp(dbus_message_get_member(message),
349 "SessionStateChanged") == 0);
350
David James3b3add52010-06-04 15:01:19 -0700351 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700352 dbus_message_iter_get_basic(&iter, &state_name);
353 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700354 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700355 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700356 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
357 }
358
359 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700360}
361
Darin Petkovf27f0362010-06-04 13:14:19 -0700362void MetricsDaemon::NetStateChanged(const char* state_name, TimeTicks ticks) {
Darin Petkov41e06232010-05-03 16:45:37 -0700363 DLOG(INFO) << "network state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700364
Darin Petkov703ec972010-04-27 11:02:18 -0700365 NetworkState state = LookupNetworkState(state_name);
366
367 // Logs the time in seconds between the network going online to
Darin Petkov2ccef012010-05-05 16:06:37 -0700368 // going offline (or, more precisely, going not online) in order to
369 // measure the mean time to network dropping. Going offline as part
370 // of suspend-to-RAM is not logged as network drop -- the assumption
371 // is that the message for suspend-to-RAM comes before the network
372 // offline message which seems to and should be the case.
373 if (state != kNetworkStateOnline &&
Darin Petkov703ec972010-04-27 11:02:18 -0700374 network_state_ == kNetworkStateOnline &&
375 power_state_ != kPowerStateMem) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700376 TimeDelta since_online = ticks - network_state_last_;
377 int online_time = static_cast<int>(since_online.InSeconds());
Darin Petkov11b8eb32010-05-18 11:00:59 -0700378 SendMetric(kMetricTimeToNetworkDropName, online_time,
379 kMetricTimeToNetworkDropMin,
380 kMetricTimeToNetworkDropMax,
381 kMetricTimeToNetworkDropBuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700382 }
383
Darin Petkov703ec972010-04-27 11:02:18 -0700384 network_state_ = state;
Darin Petkovf27f0362010-06-04 13:14:19 -0700385 network_state_last_ = ticks;
Darin Petkov65b01462010-04-14 13:32:20 -0700386}
387
Darin Petkov703ec972010-04-27 11:02:18 -0700388MetricsDaemon::NetworkState
389MetricsDaemon::LookupNetworkState(const char* state_name) {
Darin Petkov65b01462010-04-14 13:32:20 -0700390 for (int i = 0; i < kNumberNetworkStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700391 if (strcmp(state_name, kNetworkStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700392 return static_cast<NetworkState>(i);
Darin Petkov65b01462010-04-14 13:32:20 -0700393 }
394 }
Darin Petkov41e06232010-05-03 16:45:37 -0700395 DLOG(WARNING) << "unknown network connection state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700396 return kUnknownNetworkState;
397}
398
Darin Petkovf27f0362010-06-04 13:14:19 -0700399void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700400 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700401 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700402
403 if (power_state_ != kPowerStateOn)
404 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700405}
406
407MetricsDaemon::PowerState
408MetricsDaemon::LookupPowerState(const char* state_name) {
409 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700410 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700411 return static_cast<PowerState>(i);
412 }
413 }
Darin Petkov41e06232010-05-03 16:45:37 -0700414 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700415 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700416}
417
Darin Petkovf27f0362010-06-04 13:14:19 -0700418void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700419 DLOG(INFO) << "user session state: " << state_name;
420 session_state_ = LookupSessionState(state_name);
421 SetUserActiveState(session_state_ == kSessionStateStarted, now);
422}
423
424MetricsDaemon::SessionState
425MetricsDaemon::LookupSessionState(const char* state_name) {
426 for (int i = 0; i < kNumberSessionStates; i++) {
427 if (strcmp(state_name, kSessionStates_[i]) == 0) {
428 return static_cast<SessionState>(i);
429 }
430 }
431 DLOG(WARNING) << "unknown user session state: " << state_name;
432 return kUnknownSessionState;
433}
434
Darin Petkovf27f0362010-06-04 13:14:19 -0700435void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700436 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
437
438 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700439 // the day since Epoch, and logs the usage data. Guards against the
440 // time jumping back and forth due to the user changing it by
441 // discarding the new use time.
442 int seconds = 0;
443 if (user_active_ && now > user_active_last_) {
444 TimeDelta since_active = now - user_active_last_;
445 if (since_active < TimeDelta::FromSeconds(
446 kUseMonitorIntervalMax + kSecondsPerMinute)) {
447 seconds = static_cast<int>(since_active.InSeconds());
448 }
449 }
450 TimeDelta since_epoch = now - Time();
451 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700452 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700453 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700454 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700455
456 // Schedules a use monitor on inactive->active transitions and
457 // unschedules it on active->inactive transitions.
458 if (!user_active_ && active)
459 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
460 else if (user_active_ && !active)
461 UnscheduleUseMonitor();
462
463 // Remembers the current active state and the time of the last
464 // activity update.
465 user_active_ = active;
466 user_active_last_ = now;
467}
468
Darin Petkov1bb904e2010-06-16 15:58:06 -0700469void MetricsDaemon::ProcessUserCrash() {
470 // Counts the active use time up to now.
471 SetUserActiveState(user_active_, Time::Now());
472
473 // Reports the active use time since the last crash and resets it.
474 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700475
476 user_crashes_daily_->Update(1);
477 any_crashes_daily_->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700478}
479
Darin Petkov38d5cb02010-06-24 12:10:26 -0700480void MetricsDaemon::ProcessKernelCrash() {
481 // Counts the active use time up to now.
482 SetUserActiveState(user_active_, Time::Now());
483
484 // Reports the active use time since the last crash and resets it.
485 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700486
487 kernel_crashes_daily_->Update(1);
488 any_crashes_daily_->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700489}
490
Ken Mixterccd84c02010-08-16 19:57:13 -0700491void MetricsDaemon::ProcessUncleanShutdown() {
492 // Counts the active use time up to now.
493 SetUserActiveState(user_active_, Time::Now());
494
495 // Reports the active use time since the last crash and resets it.
496 unclean_shutdown_interval_->Flush();
497
498 unclean_shutdowns_daily_->Update(1);
499 any_crashes_daily_->Update(1);
500}
501
502bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700503 FilePath crash_detected(crash_file);
504 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700505 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700506
507 // Deletes the crash-detected file so that the daemon doesn't report
508 // another kernel crash in case it's restarted.
509 file_util::Delete(crash_detected,
510 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700511 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700512}
513
Darin Petkov41e06232010-05-03 16:45:37 -0700514// static
515gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
516 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
517}
518
519bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700520 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700521
522 // If a new monitor source/instance is scheduled, returns false to
523 // tell GLib to destroy this monitor source/instance. Returns true
524 // otherwise to keep calling back this monitor.
525 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
526}
527
528bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
529{
Darin Petkov2ccef012010-05-05 16:06:37 -0700530 if (testing_)
531 return false;
532
Darin Petkov41e06232010-05-03 16:45:37 -0700533 // Caps the interval -- the bigger the interval, the more active use
534 // time will be potentially dropped on system shutdown.
535 if (interval > kUseMonitorIntervalMax)
536 interval = kUseMonitorIntervalMax;
537
538 if (backoff) {
539 // Back-off mode is used by the use monitor to reschedule itself
540 // with exponential back-off in time. This mode doesn't create a
541 // new timeout source if the new interval is the same as the old
542 // one. Also, if a new timeout source is created, the old one is
543 // not destroyed explicitly here -- it will be destroyed by GLib
544 // when the monitor returns FALSE (see UseMonitor and
545 // UseMonitorStatic).
546 if (interval == usemon_interval_)
547 return false;
548 } else {
549 UnscheduleUseMonitor();
550 }
551
552 // Schedules a new use monitor for |interval| seconds from now.
553 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
554 usemon_source_ = g_timeout_source_new_seconds(interval);
555 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
556 NULL); // No destroy notification.
557 g_source_attach(usemon_source_,
558 NULL); // Default context.
559 usemon_interval_ = interval;
560 return true;
561}
562
563void MetricsDaemon::UnscheduleUseMonitor() {
564 // If there's a use monitor scheduled already, destroys it.
565 if (usemon_source_ == NULL)
566 return;
567
568 DLOG(INFO) << "destroying use monitor";
569 g_source_destroy(usemon_source_);
570 usemon_source_ = NULL;
571 usemon_interval_ = 0;
572}
573
Darin Petkovf1e85e42010-06-10 15:59:53 -0700574// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700575void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700576 if (count <= 0)
577 return;
578
Darin Petkovf1e85e42010-06-10 15:59:53 -0700579 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
580 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
581 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
582 kMetricDailyUseTimeMin,
583 kMetricDailyUseTimeMax,
584 kMetricDailyUseTimeBuckets);
585}
586
Darin Petkov1bb904e2010-06-16 15:58:06 -0700587// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700588void MetricsDaemon::ReportCrashInterval(const char* histogram_name,
589 void* handle, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700590 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
Ken Mixterccd84c02010-08-16 19:57:13 -0700591 daemon->SendMetric(histogram_name, count,
592 kMetricCrashIntervalMin,
593 kMetricCrashIntervalMax,
594 kMetricCrashIntervalBuckets);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700595}
596
Darin Petkov38d5cb02010-06-24 12:10:26 -0700597// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700598void MetricsDaemon::ReportUserCrashInterval(void* handle,
599 int tag, int count) {
600 ReportCrashInterval(kMetricUserCrashIntervalName, handle, count);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700601}
602
Ken Mixterccd84c02010-08-16 19:57:13 -0700603// static
604void MetricsDaemon::ReportKernelCrashInterval(void* handle,
605 int tag, int count) {
606 ReportCrashInterval(kMetricKernelCrashIntervalName, handle, count);
607}
608
609// static
610void MetricsDaemon::ReportUncleanShutdownInterval(void* handle,
611 int tag, int count) {
612 ReportCrashInterval(kMetricUncleanShutdownIntervalName, handle, count);
613}
614
615// static
616void MetricsDaemon::ReportCrashesDailyFrequency(const char* histogram_name,
617 void* handle,
618 int count) {
619 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
620 daemon->SendMetric(histogram_name, count,
621 kMetricCrashesDailyMin,
622 kMetricCrashesDailyMax,
623 kMetricCrashesDailyBuckets);
624}
625
626// static
627void MetricsDaemon::ReportUserCrashesDaily(void* handle,
628 int tag, int count) {
629 ReportCrashesDailyFrequency(kMetricUserCrashesDailyName, handle, count);
630}
631
632// static
633void MetricsDaemon::ReportKernelCrashesDaily(void* handle,
634 int tag, int count) {
635 ReportCrashesDailyFrequency(kMetricKernelCrashesDailyName, handle, count);
636}
637
638// static
639void MetricsDaemon::ReportUncleanShutdownsDaily(void* handle,
640 int tag, int count) {
641 ReportCrashesDailyFrequency(kMetricUncleanShutdownsDailyName, handle, count);
642}
643
644// static
645void MetricsDaemon::ReportAnyCrashesDaily(void* handle, int tag, int count) {
646 ReportCrashesDailyFrequency(kMetricAnyCrashesDailyName, handle, count);
647}
648
649
Darin Petkov38d5cb02010-06-24 12:10:26 -0700650void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700651 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700652 DLOG(INFO) << "received metric: " << name << " " << sample << " "
653 << min << " " << max << " " << nbuckets;
654 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700655}