blob: 7ce28580af861f094f54f304f7a683691c5debea [file] [log] [blame]
Darin Petkov8032dd02011-05-09 16:33:19 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Darin Petkov65b01462010-04-14 13:32:20 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "metrics_daemon.h"
Darin Petkov65b01462010-04-14 13:32:20 -07006
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -08007#include <fcntl.h>
Luigi Semenzato8accd332011-05-17 16:37:18 -07008#include <math.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -07009#include <string.h>
Luigi Semenzato8accd332011-05-17 16:37:18 -070010#include <time.h>
Darin Petkov65b01462010-04-14 13:32:20 -070011
Darin Petkov38d5cb02010-06-24 12:10:26 -070012#include <base/file_util.h>
Darin Petkov65b01462010-04-14 13:32:20 -070013#include <base/logging.h>
Luigi Semenzato29c7ef92011-04-12 14:12:35 -070014#include <base/string_util.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070015#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070016
Darin Petkovf1e85e42010-06-10 15:59:53 -070017#include "counter.h"
18
Darin Petkovf27f0362010-06-04 13:14:19 -070019using base::Time;
20using base::TimeDelta;
21using base::TimeTicks;
Luigi Semenzato8accd332011-05-17 16:37:18 -070022using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070023using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070024using std::vector;
25
Darin Petkovf27f0362010-06-04 13:14:19 -070026
Darin Petkov703ec972010-04-27 11:02:18 -070027#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov1bb904e2010-06-16 15:58:06 -070028#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter"
David James6bf6e252010-06-06 18:52:40 -070029#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager"
Darin Petkov41e06232010-05-03 16:45:37 -070030#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface"
31
Darin Petkov41e06232010-05-03 16:45:37 -070032static const int kSecondsPerMinute = 60;
33static const int kMinutesPerHour = 60;
34static const int kHoursPerDay = 24;
35static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070036static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
37static const int kDaysPerWeek = 7;
38static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070039
40// The daily use monitor is scheduled to a 1-minute interval after
41// initial user activity and then it's exponentially backed off to
42// 10-minute intervals. Although not required, the back off is
43// implemented because the histogram buckets are spaced exponentially
44// anyway and to avoid too frequent metrics daemon process wake-ups
45// and file I/O.
46static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
47static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070048
Ken Mixterccd84c02010-08-16 19:57:13 -070049const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
50static const char kUncleanShutdownDetectedFile[] =
51 "/tmp/unclean-shutdown-detected";
52
Ken Mixter4c5daa42010-08-26 18:35:06 -070053// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070054const char MetricsDaemon::kMetricDailyUseTimeName[] =
55 "Logging.DailyUseTime";
56const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
57const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
58const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
59
Ken Mixterccd84c02010-08-16 19:57:13 -070060// crash interval metrics
61const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
62 "Logging.KernelCrashInterval";
63const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
64 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070065const char MetricsDaemon::kMetricUserCrashIntervalName[] =
66 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070067
68const int MetricsDaemon::kMetricCrashIntervalMin = 1;
69const int MetricsDaemon::kMetricCrashIntervalMax =
70 4 * kSecondsPerWeek;
71const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
72
73// crash frequency metrics
74const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
75 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070076const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
77 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070078const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
79 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070080const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
81 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070082const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
83 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070084const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
85 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070086const char MetricsDaemon::kMetricUserCrashesDailyName[] =
87 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070088const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
89 "Logging.UserCrashesWeekly";
90const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
91const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
92const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070093
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080094// disk stats metrics
95
96// The {Read,Write}Sectors numbers are in sectors/second.
97// A sector is usually 512 bytes.
98
99const char MetricsDaemon::kMetricReadSectorsLongName[] =
100 "Platform.ReadSectorsLong";
101const char MetricsDaemon::kMetricWriteSectorsLongName[] =
102 "Platform.WriteSectorsLong";
103const char MetricsDaemon::kMetricReadSectorsShortName[] =
104 "Platform.ReadSectorsShort";
105const char MetricsDaemon::kMetricWriteSectorsShortName[] =
106 "Platform.WriteSectorsShort";
107
108const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds
109const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds
110
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700111const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
112
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800113// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
114// sectors.
115const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
116const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
117
Ken Mixter4c5daa42010-08-26 18:35:06 -0700118// persistent metrics path
119const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700120
Darin Petkov1bb904e2010-06-16 15:58:06 -0700121
Darin Petkov703ec972010-04-27 11:02:18 -0700122// static
Darin Petkov41e06232010-05-03 16:45:37 -0700123const char* MetricsDaemon::kDBusMatches_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700124 "type='signal',"
Darin Petkov1bb904e2010-06-16 15:58:06 -0700125 "interface='" DBUS_IFACE_CRASH_REPORTER "',"
126 "path='/',"
127 "member='UserCrash'",
128
129 "type='signal',"
Darin Petkov703ec972010-04-27 11:02:18 -0700130 "interface='" DBUS_IFACE_POWER_MANAGER "',"
Benson Leung53faeb02010-06-08 15:59:13 -0700131 "path='/'",
Darin Petkov41e06232010-05-03 16:45:37 -0700132
133 "type='signal',"
134 "sender='org.chromium.SessionManager',"
135 "interface='" DBUS_IFACE_SESSION_MANAGER "',"
136 "path='/org/chromium/SessionManager',"
137 "member='SessionStateChanged'",
Darin Petkov703ec972010-04-27 11:02:18 -0700138};
139
140// static
Darin Petkov41e06232010-05-03 16:45:37 -0700141const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700142#define STATE(name, capname) #name,
143#include "power_states.h"
144};
145
Darin Petkov41e06232010-05-03 16:45:37 -0700146// static
Darin Petkov41e06232010-05-03 16:45:37 -0700147const char* MetricsDaemon::kSessionStates_[] = {
148#define STATE(name, capname) #name,
149#include "session_states.h"
150};
151
Luigi Semenzato8accd332011-05-17 16:37:18 -0700152// Memory use stats collection intervals. We collect some memory use interval
153// at these intervals after boot, and we stop collecting after the last one,
154// with the assumption that in most cases the memory use won't change much
155// after that.
156static const int kMemuseIntervals[] = {
157 1 * kSecondsPerMinute, // 1 minute mark
158 4 * kSecondsPerMinute, // 5 minute mark
159 25 * kSecondsPerMinute, // 0.5 hour mark
160 120 * kSecondsPerMinute, // 2.5 hour mark
161 600 * kSecondsPerMinute, // 12.5 hour mark
162};
163
Darin Petkovf1e85e42010-06-10 15:59:53 -0700164MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700165 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700166 session_state_(kUnknownSessionState),
167 user_active_(false),
168 usemon_interval_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700169 usemon_source_(NULL),
170 memuse_initial_time_(0),
171 memuse_interval_index_(0),
172 read_sectors_(0),
173 write_sectors_(0),
174 diskstats_state_(kDiskStatsShort),
175 diskstats_initial_time_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700176
Ken Mixter4c5daa42010-08-26 18:35:06 -0700177MetricsDaemon::~MetricsDaemon() {
178 DeleteFrequencyCounters();
179}
180
Luigi Semenzato8accd332011-05-17 16:37:18 -0700181double MetricsDaemon::GetActiveTime() {
182 struct timespec ts;
183 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
184 if (r < 0) {
185 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
186 return 0;
187 } else {
188 return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000);
189 }
190}
191
Ken Mixter4c5daa42010-08-26 18:35:06 -0700192void MetricsDaemon::DeleteFrequencyCounters() {
193 for (FrequencyCounters::iterator i = frequency_counters_.begin();
194 i != frequency_counters_.end(); ++i) {
195 delete i->second;
196 i->second = NULL;
197 }
198}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700199
Darin Petkov2ccef012010-05-05 16:06:37 -0700200void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700201 if (run_as_daemon && daemon(0, 0) != 0)
202 return;
203
Ken Mixterccd84c02010-08-16 19:57:13 -0700204 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
205 ProcessKernelCrash();
206 }
207
208 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
209 ProcessUncleanShutdown();
210 }
211
Darin Petkov38d5cb02010-06-24 12:10:26 -0700212 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700213}
214
Ken Mixter4c5daa42010-08-26 18:35:06 -0700215FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
216 return FilePath(kMetricsPath).Append(histogram_name);
217}
218
219void MetricsDaemon::ConfigureCrashIntervalReporter(
220 const char* histogram_name,
221 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
222 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
223 FilePath file_path = GetHistogramPath(histogram_name);
224 (*reporter)->Init(file_path.value().c_str(),
225 histogram_name,
226 kMetricCrashIntervalMin,
227 kMetricCrashIntervalMax,
228 kMetricCrashIntervalBuckets);
229}
230
231void MetricsDaemon::ConfigureCrashFrequencyReporter(
232 const char* histogram_name) {
233 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
234 new chromeos_metrics::TaggedCounterReporter());
235 FilePath file_path = GetHistogramPath(histogram_name);
236 reporter->Init(file_path.value().c_str(),
237 histogram_name,
238 kMetricCrashFrequencyMin,
239 kMetricCrashFrequencyMax,
240 kMetricCrashFrequencyBuckets);
241 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
242 new chromeos_metrics::FrequencyCounter());
243 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
244 chromeos_metrics::kSecondsPerWeek :
245 chromeos_metrics::kSecondsPerDay;
246 new_counter->Init(
247 static_cast<chromeos_metrics::TaggedCounterInterface*>(
248 reporter.release()),
249 cycle_duration);
250 frequency_counters_[histogram_name] = new_counter.release();
251}
252
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800253void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700254 const string& diskstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700255 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700256 DCHECK(metrics_lib != NULL);
257 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700258 chromeos_metrics::TaggedCounterReporter::
259 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700260
261 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700262 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700263 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700264
Ken Mixter4c5daa42010-08-26 18:35:06 -0700265 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
266 &kernel_crash_interval_);
267 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
268 &unclean_shutdown_interval_);
269 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
270 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700271
Ken Mixter4c5daa42010-08-26 18:35:06 -0700272 DeleteFrequencyCounters();
273 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700274 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
275 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
276 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
277 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
278 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
279 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
280 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700281
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800282 // Don't attempt to collect disk stats if there is no disk stats file.
283 if (!diskstats_path.empty()) {
284 diskstats_path_ = diskstats_path;
285 DiskStatsReporterInit();
286 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800287
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700288 // Start collecting meminfo stats.
289 ScheduleMeminfoCallback(kMetricMeminfoInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700290 ScheduleMemuseCallback(true, 0);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700291
Darin Petkov2ccef012010-05-05 16:06:37 -0700292 // Don't setup D-Bus and GLib in test mode.
293 if (testing)
294 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700295
Darin Petkov703ec972010-04-27 11:02:18 -0700296 g_thread_init(NULL);
297 g_type_init();
298 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700299
Darin Petkov703ec972010-04-27 11:02:18 -0700300 DBusError error;
301 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700302
David James3b3add52010-06-04 15:01:19 -0700303 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700304 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
305 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700306
Darin Petkov703ec972010-04-27 11:02:18 -0700307 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700308
Darin Petkov703ec972010-04-27 11:02:18 -0700309 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov1bb904e2010-06-16 15:58:06 -0700310 for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700311 const char* match = kDBusMatches_[m];
312 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700313 dbus_bus_add_match(connection, match, &error);
314 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
315 "unable to add a match: " << SAFE_MESSAGE(error);
316 }
317
318 // Adds the D-Bus filter routine to be called back whenever one of
319 // the registered D-Bus matches is successful. The daemon is not
320 // activated for D-Bus messages that don't match.
321 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700322}
323
324void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700325 GMainLoop* loop = g_main_loop_new(NULL, false);
326 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700327}
328
Darin Petkov703ec972010-04-27 11:02:18 -0700329// static
330DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
331 DBusMessage* message,
332 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700333 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700334 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700335
336 int message_type = dbus_message_get_type(message);
337 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700338 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700339 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
340 }
341
342 // Signal messages always have interfaces.
343 const char* interface = dbus_message_get_interface(message);
344 CHECK(interface != NULL);
345
346 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
347
348 DBusMessageIter iter;
349 dbus_message_iter_init(message, &iter);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700350 if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) {
351 CHECK(strcmp(dbus_message_get_member(message),
352 "UserCrash") == 0);
353 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700354 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
David James6bf6e252010-06-06 18:52:40 -0700355 const char* member = dbus_message_get_member(message);
356 if (strcmp(member, "ScreenIsLocked") == 0) {
357 daemon->SetUserActiveState(false, now);
358 } else if (strcmp(member, "ScreenIsUnlocked") == 0) {
359 daemon->SetUserActiveState(true, now);
360 } else if (strcmp(member, "PowerStateChanged") == 0) {
361 char* state_name;
362 dbus_message_iter_get_basic(&iter, &state_name);
363 daemon->PowerStateChanged(state_name, now);
364 }
Darin Petkov41e06232010-05-03 16:45:37 -0700365 } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) {
366 CHECK(strcmp(dbus_message_get_member(message),
367 "SessionStateChanged") == 0);
368
David James3b3add52010-06-04 15:01:19 -0700369 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700370 dbus_message_iter_get_basic(&iter, &state_name);
371 daemon->SessionStateChanged(state_name, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700372 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700373 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700374 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
375 }
376
377 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700378}
379
Darin Petkovf27f0362010-06-04 13:14:19 -0700380void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700381 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700382 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700383
384 if (power_state_ != kPowerStateOn)
385 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700386}
387
388MetricsDaemon::PowerState
389MetricsDaemon::LookupPowerState(const char* state_name) {
390 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700391 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700392 return static_cast<PowerState>(i);
393 }
394 }
Darin Petkov41e06232010-05-03 16:45:37 -0700395 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700396 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700397}
398
Darin Petkovf27f0362010-06-04 13:14:19 -0700399void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700400 DLOG(INFO) << "user session state: " << state_name;
401 session_state_ = LookupSessionState(state_name);
402 SetUserActiveState(session_state_ == kSessionStateStarted, now);
403}
404
405MetricsDaemon::SessionState
406MetricsDaemon::LookupSessionState(const char* state_name) {
407 for (int i = 0; i < kNumberSessionStates; i++) {
408 if (strcmp(state_name, kSessionStates_[i]) == 0) {
409 return static_cast<SessionState>(i);
410 }
411 }
412 DLOG(WARNING) << "unknown user session state: " << state_name;
413 return kUnknownSessionState;
414}
415
Darin Petkovf27f0362010-06-04 13:14:19 -0700416void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700417 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
418
419 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700420 // the day since Epoch, and logs the usage data. Guards against the
421 // time jumping back and forth due to the user changing it by
422 // discarding the new use time.
423 int seconds = 0;
424 if (user_active_ && now > user_active_last_) {
425 TimeDelta since_active = now - user_active_last_;
426 if (since_active < TimeDelta::FromSeconds(
427 kUseMonitorIntervalMax + kSecondsPerMinute)) {
428 seconds = static_cast<int>(since_active.InSeconds());
429 }
430 }
431 TimeDelta since_epoch = now - Time();
432 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700433 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700434 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700435 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700436
Ken Mixter4c5daa42010-08-26 18:35:06 -0700437 // Flush finished cycles of all frequency counters.
438 for (FrequencyCounters::iterator i = frequency_counters_.begin();
439 i != frequency_counters_.end(); ++i) {
440 i->second->FlushFinishedCycles();
441 }
442
Darin Petkov41e06232010-05-03 16:45:37 -0700443 // Schedules a use monitor on inactive->active transitions and
444 // unschedules it on active->inactive transitions.
445 if (!user_active_ && active)
446 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
447 else if (user_active_ && !active)
448 UnscheduleUseMonitor();
449
450 // Remembers the current active state and the time of the last
451 // activity update.
452 user_active_ = active;
453 user_active_last_ = now;
454}
455
Darin Petkov1bb904e2010-06-16 15:58:06 -0700456void MetricsDaemon::ProcessUserCrash() {
457 // Counts the active use time up to now.
458 SetUserActiveState(user_active_, Time::Now());
459
460 // Reports the active use time since the last crash and resets it.
461 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700462
Ken Mixter4c5daa42010-08-26 18:35:06 -0700463 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
464 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
465 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
466 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700467}
468
Darin Petkov38d5cb02010-06-24 12:10:26 -0700469void MetricsDaemon::ProcessKernelCrash() {
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 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700475
Ken Mixter4c5daa42010-08-26 18:35:06 -0700476 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
477 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
478 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
479 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700480}
481
Ken Mixterccd84c02010-08-16 19:57:13 -0700482void MetricsDaemon::ProcessUncleanShutdown() {
483 // Counts the active use time up to now.
484 SetUserActiveState(user_active_, Time::Now());
485
486 // Reports the active use time since the last crash and resets it.
487 unclean_shutdown_interval_->Flush();
488
Ken Mixter4c5daa42010-08-26 18:35:06 -0700489 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
490 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
491 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
492 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700493}
494
Luigi Semenzato8accd332011-05-17 16:37:18 -0700495bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700496 FilePath crash_detected(crash_file);
497 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700498 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700499
500 // Deletes the crash-detected file so that the daemon doesn't report
501 // another kernel crash in case it's restarted.
502 file_util::Delete(crash_detected,
503 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700504 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700505}
506
Darin Petkov41e06232010-05-03 16:45:37 -0700507// static
508gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
509 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
510}
511
512bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700513 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700514
515 // If a new monitor source/instance is scheduled, returns false to
516 // tell GLib to destroy this monitor source/instance. Returns true
517 // otherwise to keep calling back this monitor.
518 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
519}
520
521bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
522{
Darin Petkov2ccef012010-05-05 16:06:37 -0700523 if (testing_)
524 return false;
525
Darin Petkov41e06232010-05-03 16:45:37 -0700526 // Caps the interval -- the bigger the interval, the more active use
527 // time will be potentially dropped on system shutdown.
528 if (interval > kUseMonitorIntervalMax)
529 interval = kUseMonitorIntervalMax;
530
531 if (backoff) {
532 // Back-off mode is used by the use monitor to reschedule itself
533 // with exponential back-off in time. This mode doesn't create a
534 // new timeout source if the new interval is the same as the old
535 // one. Also, if a new timeout source is created, the old one is
536 // not destroyed explicitly here -- it will be destroyed by GLib
537 // when the monitor returns FALSE (see UseMonitor and
538 // UseMonitorStatic).
539 if (interval == usemon_interval_)
540 return false;
541 } else {
542 UnscheduleUseMonitor();
543 }
544
545 // Schedules a new use monitor for |interval| seconds from now.
546 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
547 usemon_source_ = g_timeout_source_new_seconds(interval);
548 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
549 NULL); // No destroy notification.
550 g_source_attach(usemon_source_,
551 NULL); // Default context.
552 usemon_interval_ = interval;
553 return true;
554}
555
556void MetricsDaemon::UnscheduleUseMonitor() {
557 // If there's a use monitor scheduled already, destroys it.
558 if (usemon_source_ == NULL)
559 return;
560
561 DLOG(INFO) << "destroying use monitor";
562 g_source_destroy(usemon_source_);
563 usemon_source_ = NULL;
564 usemon_interval_ = 0;
565}
566
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800567void MetricsDaemon::DiskStatsReporterInit() {
568 DiskStatsReadStats(&read_sectors_, &write_sectors_);
569 // The first time around just run the long stat, so we don't delay boot.
570 diskstats_state_ = kDiskStatsLong;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700571 diskstats_initial_time_ = GetActiveTime();
572 if (diskstats_initial_time_ < 0) {
573 LOG(WARNING) << "not collecting disk stats";
574 } else {
575 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval);
576 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800577}
578
579void MetricsDaemon::ScheduleDiskStatsCallback(int wait) {
580 if (testing_) {
581 return;
582 }
583 g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this);
584}
585
586void MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
587 long int* write_sectors) {
588 int nchars;
589 int nitems;
590 char line[200];
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800591 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800592 if (file < 0) {
593 PLOG(WARNING) << "cannot open " << diskstats_path_;
594 return;
595 }
596 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
597 if (nchars < 0) {
598 PLOG(WARNING) << "cannot read from " << diskstats_path_;
599 } else {
600 LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in "
601 << diskstats_path_;
602 line[nchars] = '\0';
603 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
604 read_sectors, write_sectors);
605 LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in "
606 << diskstats_path_ << ", expected 2";
607 }
608 HANDLE_EINTR(close(file));
609}
610
611// static
612gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) {
613 (static_cast<MetricsDaemon*>(handle))->DiskStatsCallback();
614 return false; // one-time callback
615}
616
Luigi Semenzato8accd332011-05-17 16:37:18 -0700617// Collects disk stats alternating over a short and a long interval.
618
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800619void MetricsDaemon::DiskStatsCallback() {
620 long int read_sectors_now, write_sectors_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700621 double time_now = GetActiveTime();
622 double delta_time = time_now - diskstats_initial_time_;
623 if (testing_) {
624 // Fake the time when testing.
625 delta_time = diskstats_state_ == kDiskStatsShort ?
626 kMetricDiskStatsShortInterval : kMetricDiskStatsLongInterval;
627 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800628 DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700629 int delta_read = read_sectors_now - read_sectors_;
630 int delta_write = write_sectors_now - write_sectors_;
631 int read_sectors_per_second = delta_read / delta_time;
632 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800633
634 switch (diskstats_state_) {
635 case kDiskStatsShort:
636 SendMetric(kMetricReadSectorsShortName,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700637 read_sectors_per_second,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800638 1,
639 kMetricSectorsIOMax,
640 kMetricSectorsBuckets);
641 SendMetric(kMetricWriteSectorsShortName,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700642 write_sectors_per_second,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800643 1,
644 kMetricSectorsIOMax,
645 kMetricSectorsBuckets);
646 // Schedule long callback.
647 diskstats_state_ = kDiskStatsLong;
648 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval -
649 kMetricDiskStatsShortInterval);
650 break;
651 case kDiskStatsLong:
652 SendMetric(kMetricReadSectorsLongName,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700653 read_sectors_per_second,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800654 1,
655 kMetricSectorsIOMax,
656 kMetricSectorsBuckets);
657 SendMetric(kMetricWriteSectorsLongName,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700658 write_sectors_per_second,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800659 1,
660 kMetricSectorsIOMax,
661 kMetricSectorsBuckets);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700662 // Reset sector counters.
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800663 read_sectors_ = read_sectors_now;
664 write_sectors_ = write_sectors_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700665 // Set start time for new cycle.
666 diskstats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800667 // Schedule short callback.
668 diskstats_state_ = kDiskStatsShort;
669 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval);
670 break;
671 default:
672 LOG(FATAL) << "Invalid disk stats state";
673 }
674}
675
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700676void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
677 if (testing_) {
678 return;
679 }
680 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
681}
682
683// static
684gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
685 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
686}
687
688gboolean MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700689 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700690 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700691 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700692 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
693 return false;
694 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700695 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700696}
697
Luigi Semenzato8accd332011-05-17 16:37:18 -0700698gboolean MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
699 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700700 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
701 { "MemFree", "MemFree" },
702 { "Buffers", "Buffers" },
703 { "Cached", "Cached" },
704 // { "SwapCached", "SwapCached" },
705 { "Active", "Active" },
706 { "Inactive", "Inactive" },
707 { "ActiveAnon", "Active(anon)" },
708 { "InactiveAnon", "Inactive(anon)" },
709 { "ActiveFile" , "Active(file)" },
710 { "InactiveFile", "Inactive(file)" },
711 { "Unevictable", "Unevictable", 1 },
712 // { "Mlocked", "Mlocked" },
713 // { "SwapTotal", "SwapTotal" },
714 // { "SwapFree", "SwapFree" },
715 // { "Dirty", "Dirty" },
716 // { "Writeback", "Writeback" },
717 { "AnonPages", "AnonPages" },
718 { "Mapped", "Mapped" },
719 { "Shmem", "Shmem", 1 },
720 { "Slab", "Slab", 1 },
721 // { "SReclaimable", "SReclaimable" },
722 // { "SUnreclaim", "SUnreclaim" },
723 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700724 vector<MeminfoRecord> fields(fields_array,
725 fields_array + arraysize(fields_array));
726 if (!FillMeminfo(meminfo_raw, &fields)) {
727 return false;
728 }
729 int total_memory = fields[0].value;
730 if (total_memory == 0) {
731 // this "cannot happen"
732 LOG(WARNING) << "borked meminfo parser";
733 return false;
734 }
735 // Send all fields retrieved, except total memory.
736 for (unsigned int i = 1; i < fields.size(); i++) {
737 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
738 if (fields[i].log_scale) {
739 // report value in kbytes, log scale, 4Gb max
740 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
741 } else {
742 // report value as percent of total memory
743 int percent = fields[i].value * 100 / total_memory;
744 SendLinearMetric(metrics_name, percent, 100, 101);
745 }
746 }
747 return true;
748}
749
750gboolean MetricsDaemon::FillMeminfo(const string& meminfo_raw,
751 vector<MeminfoRecord>* fields) {
752 vector<string> lines;
753 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700754
755 // Scan meminfo output and collect field values. Each field name has to
756 // match a meminfo entry (case insensitive) after removing non-alpha
757 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700758 unsigned int ifield = 0;
759 for (unsigned int iline = 0;
760 iline < nlines && ifield < fields->size();
761 iline++) {
762 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700763 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700764 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
765 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700766 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700767 (*fields)[ifield].value =
768 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700769 if (*rest != '\0') {
770 LOG(WARNING) << "missing meminfo value";
771 return false;
772 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700773 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700774 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700775 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700776 if (ifield < fields->size()) {
777 // End of input reached while scanning.
778 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
779 << " and following";
780 return false;
781 }
782 return true;
783}
784
785void MetricsDaemon::ScheduleMemuseCallback(gboolean new_callback,
786 double time_elapsed) {
787 if (testing_) {
788 return;
789 }
790 int interval = kMemuseIntervals[memuse_interval_index_];
791 int wait;
792 if (new_callback) {
793 memuse_initial_time_ = GetActiveTime();
794 wait = interval;
795 } else {
796 wait = ceil(interval - time_elapsed); // round up
797 }
798 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
799}
800
801// static
802gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
803 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
804 daemon->MemuseCallback();
805 return false;
806}
807
808void MetricsDaemon::MemuseCallback() {
809 // Since we only care about active time (i.e. uptime minus sleep time) but
810 // the callbacks are driven by real time (uptime), we check if we should
811 // reschedule this callback due to intervening sleep periods.
812 double now = GetActiveTime();
813 double active_time = now - memuse_initial_time_;
814 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
815 // Not enough active time has passed. Reschedule the callback.
816 ScheduleMemuseCallback(false, active_time);
817 } else {
818 // Enough active time has passed. Do the work, and (if we succeed) see if
819 // we need to do more.
820 if (MemuseCallbackWork() &&
821 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
822 memuse_interval_index_++;
823 ScheduleMemuseCallback(true, 0);
824 }
825 }
826}
827
828gboolean MetricsDaemon::MemuseCallbackWork() {
829 string meminfo_raw;
830 const FilePath meminfo_path("/proc/meminfo");
831 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
832 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
833 return false;
834 }
835 return ProcessMemuse(meminfo_raw);
836}
837
838gboolean MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
839 static const MeminfoRecord fields_array[] = {
840 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
841 { "ActiveAnon", "Active(anon)" },
842 { "InactiveAnon", "Inactive(anon)" },
843 };
844 vector<MeminfoRecord> fields(fields_array,
845 fields_array + arraysize(fields_array));
846 if (!FillMeminfo(meminfo_raw, &fields)) {
847 return false;
848 }
849 int total = fields[0].value;
850 int active_anon = fields[1].value;
851 int inactive_anon = fields[2].value;
852 if (total == 0) {
853 // this "cannot happen"
854 LOG(WARNING) << "borked meminfo parser";
855 return false;
856 }
857 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
858 memuse_interval_index_);
859 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
860 100, 101);
861 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700862}
863
Darin Petkovf1e85e42010-06-10 15:59:53 -0700864// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700865void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700866 if (count <= 0)
867 return;
868
Darin Petkovf1e85e42010-06-10 15:59:53 -0700869 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
870 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
871 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
872 kMetricDailyUseTimeMin,
873 kMetricDailyUseTimeMax,
874 kMetricDailyUseTimeBuckets);
875}
876
Darin Petkov38d5cb02010-06-24 12:10:26 -0700877void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700878 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700879 DLOG(INFO) << "received metric: " << name << " " << sample << " "
880 << min << " " << max << " " << nbuckets;
881 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700882}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700883
884void MetricsDaemon::SendLinearMetric(const string& name, int sample,
885 int max, int nbuckets) {
886 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
887 << max << " " << nbuckets;
888 // TODO(semenzato): add a proper linear histogram to the Chrome external
889 // metrics API.
890 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
891 metrics_lib_->SendEnumToUMA(name, sample, max);
892}