blob: a7865484bf8204172a5a98ad4d28f03968243ac8 [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>
Mike Frysinger71ebf982012-03-07 10:35:29 -050015#include <base/stringprintf.h>
Darin Petkov40f25732013-04-29 15:07:31 +020016#include <chromeos/dbus/service_constants.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070017#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -070018
Darin Petkovf1e85e42010-06-10 15:59:53 -070019#include "counter.h"
20
Darin Petkovf27f0362010-06-04 13:14:19 -070021using base::Time;
22using base::TimeDelta;
23using base::TimeTicks;
Luigi Semenzato8accd332011-05-17 16:37:18 -070024using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070025using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070026using std::vector;
27
Darin Petkovf27f0362010-06-04 13:14:19 -070028
Darin Petkov703ec972010-04-27 11:02:18 -070029#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov40f25732013-04-29 15:07:31 +020030
31static const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
32static const char kCrashReporterUserCrashSignal[] = "UserCrash";
Darin Petkov41e06232010-05-03 16:45:37 -070033
Darin Petkov41e06232010-05-03 16:45:37 -070034static const int kSecondsPerMinute = 60;
35static const int kMinutesPerHour = 60;
36static const int kHoursPerDay = 24;
37static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
Darin Petkov1bb904e2010-06-16 15:58:06 -070038static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
39static const int kDaysPerWeek = 7;
40static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070041
42// The daily use monitor is scheduled to a 1-minute interval after
43// initial user activity and then it's exponentially backed off to
44// 10-minute intervals. Although not required, the back off is
45// implemented because the histogram buckets are spaced exponentially
46// anyway and to avoid too frequent metrics daemon process wake-ups
47// and file I/O.
48static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute;
49static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute;
Darin Petkov65b01462010-04-14 13:32:20 -070050
Ken Mixterccd84c02010-08-16 19:57:13 -070051const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected";
52static const char kUncleanShutdownDetectedFile[] =
53 "/tmp/unclean-shutdown-detected";
54
Ken Mixter4c5daa42010-08-26 18:35:06 -070055// static metrics parameters
Darin Petkov2ccef012010-05-05 16:06:37 -070056const char MetricsDaemon::kMetricDailyUseTimeName[] =
57 "Logging.DailyUseTime";
58const int MetricsDaemon::kMetricDailyUseTimeMin = 1;
59const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay;
60const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50;
61
Ken Mixterccd84c02010-08-16 19:57:13 -070062// crash interval metrics
63const char MetricsDaemon::kMetricKernelCrashIntervalName[] =
64 "Logging.KernelCrashInterval";
65const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] =
66 "Logging.UncleanShutdownInterval";
Darin Petkov1bb904e2010-06-16 15:58:06 -070067const char MetricsDaemon::kMetricUserCrashIntervalName[] =
68 "Logging.UserCrashInterval";
Ken Mixterccd84c02010-08-16 19:57:13 -070069
70const int MetricsDaemon::kMetricCrashIntervalMin = 1;
71const int MetricsDaemon::kMetricCrashIntervalMax =
72 4 * kSecondsPerWeek;
73const int MetricsDaemon::kMetricCrashIntervalBuckets = 50;
74
75// crash frequency metrics
76const char MetricsDaemon::kMetricAnyCrashesDailyName[] =
77 "Logging.AnyCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070078const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] =
79 "Logging.AnyCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070080const char MetricsDaemon::kMetricKernelCrashesDailyName[] =
81 "Logging.KernelCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070082const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] =
83 "Logging.KernelCrashesWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070084const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] =
85 "Logging.UncleanShutdownsDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070086const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] =
87 "Logging.UncleanShutdownsWeekly";
Ken Mixterccd84c02010-08-16 19:57:13 -070088const char MetricsDaemon::kMetricUserCrashesDailyName[] =
89 "Logging.UserCrashesDaily";
Ken Mixter4c5daa42010-08-26 18:35:06 -070090const char MetricsDaemon::kMetricUserCrashesWeeklyName[] =
91 "Logging.UserCrashesWeekly";
92const char MetricsDaemon::kMetricCrashFrequencyMin = 1;
93const char MetricsDaemon::kMetricCrashFrequencyMax = 100;
94const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50;
Ken Mixterccd84c02010-08-16 19:57:13 -070095
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080096// disk stats metrics
97
98// The {Read,Write}Sectors numbers are in sectors/second.
99// A sector is usually 512 bytes.
100
101const char MetricsDaemon::kMetricReadSectorsLongName[] =
102 "Platform.ReadSectorsLong";
103const char MetricsDaemon::kMetricWriteSectorsLongName[] =
104 "Platform.WriteSectorsLong";
105const char MetricsDaemon::kMetricReadSectorsShortName[] =
106 "Platform.ReadSectorsShort";
107const char MetricsDaemon::kMetricWriteSectorsShortName[] =
108 "Platform.WriteSectorsShort";
109
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700110const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
111const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800112
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700113const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
114
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800115// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
116// sectors.
117const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
118const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700119// Page size is 4k, sector size is 0.5k. We're not interested in page fault
120// rates that the disk cannot sustain.
121const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
122const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
123
124// Major page faults, i.e. the ones that require data to be read from disk.
125
126const char MetricsDaemon::kMetricPageFaultsLongName[] =
127 "Platform.PageFaultsLong";
128const char MetricsDaemon::kMetricPageFaultsShortName[] =
129 "Platform.PageFaultsShort";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800130
Ken Mixter4c5daa42010-08-26 18:35:06 -0700131// persistent metrics path
132const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
Ken Mixterccd84c02010-08-16 19:57:13 -0700133
Darin Petkov703ec972010-04-27 11:02:18 -0700134// static
Darin Petkov41e06232010-05-03 16:45:37 -0700135const char* MetricsDaemon::kPowerStates_[] = {
Darin Petkov703ec972010-04-27 11:02:18 -0700136#define STATE(name, capname) #name,
137#include "power_states.h"
138};
139
Darin Petkov41e06232010-05-03 16:45:37 -0700140// static
Darin Petkov41e06232010-05-03 16:45:37 -0700141const char* MetricsDaemon::kSessionStates_[] = {
142#define STATE(name, capname) #name,
143#include "session_states.h"
144};
145
Luigi Semenzato8accd332011-05-17 16:37:18 -0700146// Memory use stats collection intervals. We collect some memory use interval
147// at these intervals after boot, and we stop collecting after the last one,
148// with the assumption that in most cases the memory use won't change much
149// after that.
150static const int kMemuseIntervals[] = {
151 1 * kSecondsPerMinute, // 1 minute mark
152 4 * kSecondsPerMinute, // 5 minute mark
153 25 * kSecondsPerMinute, // 0.5 hour mark
154 120 * kSecondsPerMinute, // 2.5 hour mark
155 600 * kSecondsPerMinute, // 12.5 hour mark
156};
157
Darin Petkovf1e85e42010-06-10 15:59:53 -0700158MetricsDaemon::MetricsDaemon()
Sam Leffler239b8262010-08-30 08:56:58 -0700159 : power_state_(kUnknownPowerState),
Darin Petkovf1e85e42010-06-10 15:59:53 -0700160 session_state_(kUnknownSessionState),
161 user_active_(false),
162 usemon_interval_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700163 usemon_source_(NULL),
164 memuse_initial_time_(0),
165 memuse_interval_index_(0),
166 read_sectors_(0),
167 write_sectors_(0),
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700168 page_faults_(0),
169 stats_state_(kStatsShort),
170 stats_initial_time_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700171
Ken Mixter4c5daa42010-08-26 18:35:06 -0700172MetricsDaemon::~MetricsDaemon() {
173 DeleteFrequencyCounters();
174}
175
Luigi Semenzato8accd332011-05-17 16:37:18 -0700176double MetricsDaemon::GetActiveTime() {
177 struct timespec ts;
178 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
179 if (r < 0) {
180 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
181 return 0;
182 } else {
183 return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000);
184 }
185}
186
Ken Mixter4c5daa42010-08-26 18:35:06 -0700187void MetricsDaemon::DeleteFrequencyCounters() {
188 for (FrequencyCounters::iterator i = frequency_counters_.begin();
189 i != frequency_counters_.end(); ++i) {
190 delete i->second;
191 i->second = NULL;
192 }
193}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700194
Darin Petkov2ccef012010-05-05 16:06:37 -0700195void MetricsDaemon::Run(bool run_as_daemon) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700196 if (run_as_daemon && daemon(0, 0) != 0)
197 return;
198
Ken Mixterccd84c02010-08-16 19:57:13 -0700199 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
200 ProcessKernelCrash();
201 }
202
203 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
204 ProcessUncleanShutdown();
205 }
206
Darin Petkov38d5cb02010-06-24 12:10:26 -0700207 Loop();
Darin Petkov65b01462010-04-14 13:32:20 -0700208}
209
Ken Mixter4c5daa42010-08-26 18:35:06 -0700210FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) {
211 return FilePath(kMetricsPath).Append(histogram_name);
212}
213
214void MetricsDaemon::ConfigureCrashIntervalReporter(
215 const char* histogram_name,
216 scoped_ptr<chromeos_metrics::TaggedCounterReporter>* reporter) {
217 reporter->reset(new chromeos_metrics::TaggedCounterReporter());
218 FilePath file_path = GetHistogramPath(histogram_name);
219 (*reporter)->Init(file_path.value().c_str(),
220 histogram_name,
221 kMetricCrashIntervalMin,
222 kMetricCrashIntervalMax,
223 kMetricCrashIntervalBuckets);
224}
225
226void MetricsDaemon::ConfigureCrashFrequencyReporter(
227 const char* histogram_name) {
228 scoped_ptr<chromeos_metrics::TaggedCounterReporter> reporter(
229 new chromeos_metrics::TaggedCounterReporter());
230 FilePath file_path = GetHistogramPath(histogram_name);
231 reporter->Init(file_path.value().c_str(),
232 histogram_name,
233 kMetricCrashFrequencyMin,
234 kMetricCrashFrequencyMax,
235 kMetricCrashFrequencyBuckets);
236 scoped_ptr<chromeos_metrics::FrequencyCounter> new_counter(
237 new chromeos_metrics::FrequencyCounter());
238 time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ?
239 chromeos_metrics::kSecondsPerWeek :
240 chromeos_metrics::kSecondsPerDay;
241 new_counter->Init(
242 static_cast<chromeos_metrics::TaggedCounterInterface*>(
243 reporter.release()),
244 cycle_duration);
245 frequency_counters_[histogram_name] = new_counter.release();
246}
247
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800248void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700249 const string& diskstats_path,
250 const string& vmstats_path) {
Darin Petkov65b01462010-04-14 13:32:20 -0700251 testing_ = testing;
Darin Petkovfc91b422010-05-12 13:05:45 -0700252 DCHECK(metrics_lib != NULL);
253 metrics_lib_ = metrics_lib;
Ken Mixter4c5daa42010-08-26 18:35:06 -0700254 chromeos_metrics::TaggedCounterReporter::
255 SetMetricsLibraryInterface(metrics_lib);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700256
257 static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage";
Darin Petkovf1e85e42010-06-10 15:59:53 -0700258 daily_use_.reset(new chromeos_metrics::TaggedCounter());
Ken Mixterccd84c02010-08-16 19:57:13 -0700259 daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700260
Ken Mixter4c5daa42010-08-26 18:35:06 -0700261 ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName,
262 &kernel_crash_interval_);
263 ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName,
264 &unclean_shutdown_interval_);
265 ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName,
266 &user_crash_interval_);
Darin Petkov2ccef012010-05-05 16:06:37 -0700267
Ken Mixter4c5daa42010-08-26 18:35:06 -0700268 DeleteFrequencyCounters();
269 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
Ken Mixter4c5daa42010-08-26 18:35:06 -0700270 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
271 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
272 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
273 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName);
274 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName);
275 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
276 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700277
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700278 diskstats_path_ = diskstats_path;
279 vmstats_path_ = vmstats_path;
280 StatsReporterInit();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800281
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700282 // Start collecting meminfo stats.
283 ScheduleMeminfoCallback(kMetricMeminfoInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700284 ScheduleMemuseCallback(true, 0);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700285
Darin Petkov2ccef012010-05-05 16:06:37 -0700286 // Don't setup D-Bus and GLib in test mode.
287 if (testing)
288 return;
Darin Petkov65b01462010-04-14 13:32:20 -0700289
Darin Petkov703ec972010-04-27 11:02:18 -0700290 g_thread_init(NULL);
291 g_type_init();
292 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -0700293
Darin Petkov703ec972010-04-27 11:02:18 -0700294 DBusError error;
295 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -0700296
David James3b3add52010-06-04 15:01:19 -0700297 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
Darin Petkov703ec972010-04-27 11:02:18 -0700298 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
299 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -0700300
Darin Petkov703ec972010-04-27 11:02:18 -0700301 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -0700302
Darin Petkov40f25732013-04-29 15:07:31 +0200303 vector<string> matches;
304 matches.push_back(
305 StringPrintf("type='signal',interface='%s',path='/',member='%s'",
306 kCrashReporterInterface,
307 kCrashReporterUserCrashSignal));
308 matches.push_back(
309 StringPrintf("type='signal',interface='%s',path='%s',member='%s'",
310 power_manager::kPowerManagerInterface,
311 power_manager::kPowerManagerServicePath,
312 power_manager::kPowerStateChangedSignal));
313 matches.push_back(
314 StringPrintf("type='signal',sender='%s',interface='%s',path='%s'",
315 login_manager::kSessionManagerServiceName,
316 login_manager::kSessionManagerInterface,
317 login_manager::kSessionManagerServicePath));
318
Darin Petkov703ec972010-04-27 11:02:18 -0700319 // Registers D-Bus matches for the signals we would like to catch.
Darin Petkov40f25732013-04-29 15:07:31 +0200320 for (vector<string>::const_iterator it = matches.begin();
321 it != matches.end(); ++it) {
322 const char* match = it->c_str();
Darin Petkov41e06232010-05-03 16:45:37 -0700323 DLOG(INFO) << "adding dbus match: " << match;
Darin Petkov703ec972010-04-27 11:02:18 -0700324 dbus_bus_add_match(connection, match, &error);
325 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
326 "unable to add a match: " << SAFE_MESSAGE(error);
327 }
328
329 // Adds the D-Bus filter routine to be called back whenever one of
330 // the registered D-Bus matches is successful. The daemon is not
331 // activated for D-Bus messages that don't match.
332 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -0700333}
334
335void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -0700336 GMainLoop* loop = g_main_loop_new(NULL, false);
337 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -0700338}
339
Darin Petkov703ec972010-04-27 11:02:18 -0700340// static
341DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
342 DBusMessage* message,
343 void* user_data) {
Darin Petkovf27f0362010-06-04 13:14:19 -0700344 Time now = Time::Now();
Darin Petkovf27f0362010-06-04 13:14:19 -0700345 DLOG(INFO) << "message intercepted @ " << now.ToInternalValue();
Darin Petkov703ec972010-04-27 11:02:18 -0700346
347 int message_type = dbus_message_get_type(message);
348 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700349 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700350 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
351 }
352
353 // Signal messages always have interfaces.
354 const char* interface = dbus_message_get_interface(message);
355 CHECK(interface != NULL);
356
357 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
358
359 DBusMessageIter iter;
360 dbus_message_iter_init(message, &iter);
Darin Petkov40f25732013-04-29 15:07:31 +0200361 if (strcmp(interface, kCrashReporterInterface) == 0) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700362 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200363 kCrashReporterUserCrashSignal) == 0);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700364 daemon->ProcessUserCrash();
Darin Petkov40f25732013-04-29 15:07:31 +0200365 } else if (strcmp(interface, power_manager::kPowerManagerInterface) == 0) {
Darin Petkov41e06232010-05-03 16:45:37 -0700366 CHECK(strcmp(dbus_message_get_member(message),
Darin Petkov40f25732013-04-29 15:07:31 +0200367 power_manager::kPowerStateChangedSignal) == 0);
David James3b3add52010-06-04 15:01:19 -0700368 char* state_name;
Darin Petkov41e06232010-05-03 16:45:37 -0700369 dbus_message_iter_get_basic(&iter, &state_name);
Darin Petkov40f25732013-04-29 15:07:31 +0200370 daemon->PowerStateChanged(state_name, now);
371 } else if (strcmp(interface, login_manager::kSessionManagerInterface) == 0) {
372 const char* member = dbus_message_get_member(message);
373 if (strcmp(member, login_manager::kScreenIsLockedSignal) == 0) {
374 daemon->SetUserActiveState(false, now);
375 } else if (strcmp(member, login_manager::kScreenIsUnlockedSignal) == 0) {
376 daemon->SetUserActiveState(true, now);
377 } else if (strcmp(member, login_manager::kSessionStateChangedSignal) == 0) {
378 char* state_name;
379 dbus_message_iter_get_basic(&iter, &state_name);
380 daemon->SessionStateChanged(state_name, now);
381 }
Darin Petkov703ec972010-04-27 11:02:18 -0700382 } else {
Darin Petkov41e06232010-05-03 16:45:37 -0700383 DLOG(WARNING) << "unexpected interface: " << interface;
Darin Petkov703ec972010-04-27 11:02:18 -0700384 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
385 }
386
387 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700388}
389
Darin Petkovf27f0362010-06-04 13:14:19 -0700390void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700391 DLOG(INFO) << "power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700392 power_state_ = LookupPowerState(state_name);
Darin Petkov41e06232010-05-03 16:45:37 -0700393
394 if (power_state_ != kPowerStateOn)
395 SetUserActiveState(false, now);
Darin Petkov703ec972010-04-27 11:02:18 -0700396}
397
398MetricsDaemon::PowerState
399MetricsDaemon::LookupPowerState(const char* state_name) {
400 for (int i = 0; i < kNumberPowerStates; i++) {
Darin Petkov41e06232010-05-03 16:45:37 -0700401 if (strcmp(state_name, kPowerStates_[i]) == 0) {
Darin Petkov703ec972010-04-27 11:02:18 -0700402 return static_cast<PowerState>(i);
403 }
404 }
Darin Petkov41e06232010-05-03 16:45:37 -0700405 DLOG(WARNING) << "unknown power state: " << state_name;
Darin Petkov703ec972010-04-27 11:02:18 -0700406 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700407}
408
Darin Petkovf27f0362010-06-04 13:14:19 -0700409void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700410 DLOG(INFO) << "user session state: " << state_name;
411 session_state_ = LookupSessionState(state_name);
412 SetUserActiveState(session_state_ == kSessionStateStarted, now);
413}
414
415MetricsDaemon::SessionState
416MetricsDaemon::LookupSessionState(const char* state_name) {
417 for (int i = 0; i < kNumberSessionStates; i++) {
418 if (strcmp(state_name, kSessionStates_[i]) == 0) {
419 return static_cast<SessionState>(i);
420 }
421 }
422 DLOG(WARNING) << "unknown user session state: " << state_name;
423 return kUnknownSessionState;
424}
425
Darin Petkovf27f0362010-06-04 13:14:19 -0700426void MetricsDaemon::SetUserActiveState(bool active, Time now) {
Darin Petkov41e06232010-05-03 16:45:37 -0700427 DLOG(INFO) << "user: " << (active ? "active" : "inactive");
428
429 // Calculates the seconds of active use since the last update and
Darin Petkovf27f0362010-06-04 13:14:19 -0700430 // the day since Epoch, and logs the usage data. Guards against the
431 // time jumping back and forth due to the user changing it by
432 // discarding the new use time.
433 int seconds = 0;
434 if (user_active_ && now > user_active_last_) {
435 TimeDelta since_active = now - user_active_last_;
436 if (since_active < TimeDelta::FromSeconds(
437 kUseMonitorIntervalMax + kSecondsPerMinute)) {
438 seconds = static_cast<int>(since_active.InSeconds());
439 }
440 }
441 TimeDelta since_epoch = now - Time();
442 int day = since_epoch.InDays();
Darin Petkovf1e85e42010-06-10 15:59:53 -0700443 daily_use_->Update(day, seconds);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700444 user_crash_interval_->Update(0, seconds);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700445 kernel_crash_interval_->Update(0, seconds);
Darin Petkov41e06232010-05-03 16:45:37 -0700446
Ken Mixter4c5daa42010-08-26 18:35:06 -0700447 // Flush finished cycles of all frequency counters.
448 for (FrequencyCounters::iterator i = frequency_counters_.begin();
449 i != frequency_counters_.end(); ++i) {
450 i->second->FlushFinishedCycles();
451 }
452
Darin Petkov41e06232010-05-03 16:45:37 -0700453 // Schedules a use monitor on inactive->active transitions and
454 // unschedules it on active->inactive transitions.
455 if (!user_active_ && active)
456 ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false);
457 else if (user_active_ && !active)
458 UnscheduleUseMonitor();
459
460 // Remembers the current active state and the time of the last
461 // activity update.
462 user_active_ = active;
463 user_active_last_ = now;
464}
465
Darin Petkov1bb904e2010-06-16 15:58:06 -0700466void MetricsDaemon::ProcessUserCrash() {
467 // Counts the active use time up to now.
468 SetUserActiveState(user_active_, Time::Now());
469
470 // Reports the active use time since the last crash and resets it.
471 user_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700472
Ken Mixter4c5daa42010-08-26 18:35:06 -0700473 frequency_counters_[kMetricUserCrashesDailyName]->Update(1);
474 frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1);
475 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
476 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700477}
478
Darin Petkov38d5cb02010-06-24 12:10:26 -0700479void MetricsDaemon::ProcessKernelCrash() {
480 // Counts the active use time up to now.
481 SetUserActiveState(user_active_, Time::Now());
482
483 // Reports the active use time since the last crash and resets it.
484 kernel_crash_interval_->Flush();
Ken Mixterccd84c02010-08-16 19:57:13 -0700485
Ken Mixter4c5daa42010-08-26 18:35:06 -0700486 frequency_counters_[kMetricKernelCrashesDailyName]->Update(1);
487 frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1);
488 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
489 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700490}
491
Ken Mixterccd84c02010-08-16 19:57:13 -0700492void MetricsDaemon::ProcessUncleanShutdown() {
493 // Counts the active use time up to now.
494 SetUserActiveState(user_active_, Time::Now());
495
496 // Reports the active use time since the last crash and resets it.
497 unclean_shutdown_interval_->Flush();
498
Ken Mixter4c5daa42010-08-26 18:35:06 -0700499 frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1);
500 frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1);
501 frequency_counters_[kMetricAnyCrashesDailyName]->Update(1);
502 frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700503}
504
Luigi Semenzato8accd332011-05-17 16:37:18 -0700505bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700506 FilePath crash_detected(crash_file);
507 if (!file_util::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700508 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700509
510 // Deletes the crash-detected file so that the daemon doesn't report
511 // another kernel crash in case it's restarted.
512 file_util::Delete(crash_detected,
513 false); // recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700514 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700515}
516
Darin Petkov41e06232010-05-03 16:45:37 -0700517// static
518gboolean MetricsDaemon::UseMonitorStatic(gpointer data) {
519 return static_cast<MetricsDaemon*>(data)->UseMonitor() ? TRUE : FALSE;
520}
521
522bool MetricsDaemon::UseMonitor() {
Darin Petkovf27f0362010-06-04 13:14:19 -0700523 SetUserActiveState(user_active_, Time::Now());
Darin Petkov41e06232010-05-03 16:45:37 -0700524
525 // If a new monitor source/instance is scheduled, returns false to
526 // tell GLib to destroy this monitor source/instance. Returns true
527 // otherwise to keep calling back this monitor.
528 return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true);
529}
530
531bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff)
532{
Darin Petkov2ccef012010-05-05 16:06:37 -0700533 if (testing_)
534 return false;
535
Darin Petkov41e06232010-05-03 16:45:37 -0700536 // Caps the interval -- the bigger the interval, the more active use
537 // time will be potentially dropped on system shutdown.
538 if (interval > kUseMonitorIntervalMax)
539 interval = kUseMonitorIntervalMax;
540
541 if (backoff) {
542 // Back-off mode is used by the use monitor to reschedule itself
543 // with exponential back-off in time. This mode doesn't create a
544 // new timeout source if the new interval is the same as the old
545 // one. Also, if a new timeout source is created, the old one is
546 // not destroyed explicitly here -- it will be destroyed by GLib
547 // when the monitor returns FALSE (see UseMonitor and
548 // UseMonitorStatic).
549 if (interval == usemon_interval_)
550 return false;
551 } else {
552 UnscheduleUseMonitor();
553 }
554
555 // Schedules a new use monitor for |interval| seconds from now.
556 DLOG(INFO) << "scheduling use monitor in " << interval << " seconds";
557 usemon_source_ = g_timeout_source_new_seconds(interval);
558 g_source_set_callback(usemon_source_, UseMonitorStatic, this,
559 NULL); // No destroy notification.
560 g_source_attach(usemon_source_,
561 NULL); // Default context.
562 usemon_interval_ = interval;
563 return true;
564}
565
566void MetricsDaemon::UnscheduleUseMonitor() {
567 // If there's a use monitor scheduled already, destroys it.
568 if (usemon_source_ == NULL)
569 return;
570
571 DLOG(INFO) << "destroying use monitor";
572 g_source_destroy(usemon_source_);
573 usemon_source_ = NULL;
574 usemon_interval_ = 0;
575}
576
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700577void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800578 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700579 VmStatsReadStats(&page_faults_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800580 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700581 stats_state_ = kStatsLong;
582 stats_initial_time_ = GetActiveTime();
583 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700584 LOG(WARNING) << "not collecting disk stats";
585 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700586 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700587 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800588}
589
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700590void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800591 if (testing_) {
592 return;
593 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700594 g_timeout_add_seconds(wait, StatsCallbackStatic, this);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800595}
596
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700597bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800598 long int* write_sectors) {
599 int nchars;
600 int nitems;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700601 bool success = false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800602 char line[200];
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700603 if (diskstats_path_.empty()) {
604 return false;
605 }
Luigi Semenzato0f132bb2011-02-28 11:17:43 -0800606 int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800607 if (file < 0) {
608 PLOG(WARNING) << "cannot open " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700609 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800610 }
611 nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
612 if (nchars < 0) {
613 PLOG(WARNING) << "cannot read from " << diskstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700614 return false;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800615 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700616 LOG_IF(WARNING, nchars == sizeof(line))
617 << "line too long in " << diskstats_path_;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800618 line[nchars] = '\0';
619 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
620 read_sectors, write_sectors);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700621 if (nitems == 2) {
622 success = true;
623 } else {
624 LOG(WARNING) << "found " << nitems << " items in "
625 << diskstats_path_ << ", expected 2";
626 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800627 }
628 HANDLE_EINTR(close(file));
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700629 return success;
630}
631
632bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
633 static const char kPageFaultSearchString[] = "\npgmajfault ";
634 bool success = false;
635 /* Each line in the file has the form
636 * <ID> <VALUE>
637 * for instance:
638 * nr_free_pages 213427
639 */
640 char* s = strstr(stats, kPageFaultSearchString);
641 if (s == NULL) {
642 LOG(WARNING) << "cannot find page fault entry in vmstats";
643 } else {
644 char* endp;
645 /* Skip <ID> and space. Don't count the terminating null. */
646 s += sizeof(kPageFaultSearchString) - 1;
647 *page_faults = strtol(s, &endp, 10);
648 if (*endp == '\n') {
649 success = true;
650 } else {
651 LOG(WARNING) << "error parsing vmstats";
652 }
653 }
654 return success;
655}
656
657bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
658 char buffer[4000];
659 int nchars;
660 int success = false;
661 if (testing_) {
662 return false;
663 }
664 int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
665 if (file < 0) {
666 PLOG(WARNING) << "cannot open " << vmstats_path_;
667 return false;
668 }
669 nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
670 LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
671 << "file too large in " << vmstats_path_;
672 if (nchars < 0) {
673 PLOG(WARNING) << "cannot read from " << vmstats_path_;
674 } else if (nchars == 0) {
675 LOG(WARNING) << vmstats_path_ << " is empty";
676 } else {
677 buffer[nchars] = '\0';
678 success = VmStatsParseStats(buffer, page_faults);
679 }
680 HANDLE_EINTR(close(file));
681 return success;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800682}
683
684// static
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700685gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
686 (static_cast<MetricsDaemon*>(handle))->StatsCallback();
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800687 return false; // one-time callback
688}
689
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700690// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700691
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700692void MetricsDaemon::StatsCallback() {
693 long int read_sectors_now, write_sectors_now, page_faults_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700694 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700695 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700696 if (testing_) {
697 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700698 delta_time = stats_state_ == kStatsShort ?
699 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700700 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700701 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
702 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700703 int delta_read = read_sectors_now - read_sectors_;
704 int delta_write = write_sectors_now - write_sectors_;
705 int read_sectors_per_second = delta_read / delta_time;
706 int write_sectors_per_second = delta_write / delta_time;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700707 bool vmstats_success = VmStatsReadStats(&page_faults_now);
708 int delta_faults = page_faults_now - page_faults_;
709 int page_faults_per_second = delta_faults / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800710
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700711 switch (stats_state_) {
712 case kStatsShort:
713 if (diskstats_success) {
714 SendMetric(kMetricReadSectorsShortName,
715 read_sectors_per_second,
716 1,
717 kMetricSectorsIOMax,
718 kMetricSectorsBuckets);
719 SendMetric(kMetricWriteSectorsShortName,
720 write_sectors_per_second,
721 1,
722 kMetricSectorsIOMax,
723 kMetricSectorsBuckets);
724 }
725 if (vmstats_success) {
726 SendMetric(kMetricPageFaultsShortName,
727 page_faults_per_second,
728 1,
729 kMetricPageFaultsMax,
730 kMetricPageFaultsBuckets);
731 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800732 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700733 stats_state_ = kStatsLong;
734 ScheduleStatsCallback(kMetricStatsLongInterval -
735 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800736 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700737 case kStatsLong:
738 if (diskstats_success) {
739 SendMetric(kMetricReadSectorsLongName,
740 read_sectors_per_second,
741 1,
742 kMetricSectorsIOMax,
743 kMetricSectorsBuckets);
744 SendMetric(kMetricWriteSectorsLongName,
745 write_sectors_per_second,
746 1,
747 kMetricSectorsIOMax,
748 kMetricSectorsBuckets);
749 // Reset sector counters.
750 read_sectors_ = read_sectors_now;
751 write_sectors_ = write_sectors_now;
752 }
753 if (vmstats_success) {
754 SendMetric(kMetricPageFaultsLongName,
755 page_faults_per_second,
756 1,
757 kMetricPageFaultsMax,
758 kMetricPageFaultsBuckets);
759 page_faults_ = page_faults_now;
760 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700761 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700762 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800763 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700764 stats_state_ = kStatsShort;
765 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800766 break;
767 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700768 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800769 }
770}
771
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700772void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
773 if (testing_) {
774 return;
775 }
776 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this);
777}
778
779// static
780gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) {
781 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
782}
783
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700784bool MetricsDaemon::MeminfoCallback() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700785 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700786 const FilePath meminfo_path("/proc/meminfo");
Luigi Semenzato8accd332011-05-17 16:37:18 -0700787 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700788 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
789 return false;
790 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700791 return ProcessMeminfo(meminfo_raw);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700792}
793
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700794bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700795 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700796 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
797 { "MemFree", "MemFree" },
798 { "Buffers", "Buffers" },
799 { "Cached", "Cached" },
800 // { "SwapCached", "SwapCached" },
801 { "Active", "Active" },
802 { "Inactive", "Inactive" },
803 { "ActiveAnon", "Active(anon)" },
804 { "InactiveAnon", "Inactive(anon)" },
805 { "ActiveFile" , "Active(file)" },
806 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800807 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700808 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800809 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
810 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700811 // { "Dirty", "Dirty" },
812 // { "Writeback", "Writeback" },
813 { "AnonPages", "AnonPages" },
814 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800815 { "Shmem", "Shmem", kMeminfoOp_HistLog },
816 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700817 // { "SReclaimable", "SReclaimable" },
818 // { "SUnreclaim", "SUnreclaim" },
819 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700820 vector<MeminfoRecord> fields(fields_array,
821 fields_array + arraysize(fields_array));
822 if (!FillMeminfo(meminfo_raw, &fields)) {
823 return false;
824 }
825 int total_memory = fields[0].value;
826 if (total_memory == 0) {
827 // this "cannot happen"
828 LOG(WARNING) << "borked meminfo parser";
829 return false;
830 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800831 int swap_total = 0;
832 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700833 // Send all fields retrieved, except total memory.
834 for (unsigned int i = 1; i < fields.size(); i++) {
835 string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800836 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800837 switch (fields[i].op) {
838 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800839 // report value as percent of total memory
840 percent = fields[i].value * 100 / total_memory;
841 SendLinearMetric(metrics_name, percent, 100, 101);
842 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800843 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800844 // report value in kbytes, log scale, 4Gb max
845 SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
846 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800847 case kMeminfoOp_SwapTotal:
848 swap_total = fields[i].value;
849 case kMeminfoOp_SwapFree:
850 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800851 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700852 }
853 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800854 if (swap_total > 0) {
855 int swap_used = swap_total - swap_free;
856 int swap_used_percent = swap_used * 100 / swap_total;
857 SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
858 SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent,
859 100, 101);
860 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700861 return true;
862}
863
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700864bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
865 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700866 vector<string> lines;
867 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700868
869 // Scan meminfo output and collect field values. Each field name has to
870 // match a meminfo entry (case insensitive) after removing non-alpha
871 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700872 unsigned int ifield = 0;
873 for (unsigned int iline = 0;
874 iline < nlines && ifield < fields->size();
875 iline++) {
876 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700877 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700878 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
879 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700880 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700881 (*fields)[ifield].value =
882 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700883 if (*rest != '\0') {
884 LOG(WARNING) << "missing meminfo value";
885 return false;
886 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700887 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700888 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700889 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700890 if (ifield < fields->size()) {
891 // End of input reached while scanning.
892 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
893 << " and following";
894 return false;
895 }
896 return true;
897}
898
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700899void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700900 double time_elapsed) {
901 if (testing_) {
902 return;
903 }
904 int interval = kMemuseIntervals[memuse_interval_index_];
905 int wait;
906 if (new_callback) {
907 memuse_initial_time_ = GetActiveTime();
908 wait = interval;
909 } else {
910 wait = ceil(interval - time_elapsed); // round up
911 }
912 g_timeout_add_seconds(wait, MemuseCallbackStatic, this);
913}
914
915// static
916gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) {
917 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
918 daemon->MemuseCallback();
919 return false;
920}
921
922void MetricsDaemon::MemuseCallback() {
923 // Since we only care about active time (i.e. uptime minus sleep time) but
924 // the callbacks are driven by real time (uptime), we check if we should
925 // reschedule this callback due to intervening sleep periods.
926 double now = GetActiveTime();
927 double active_time = now - memuse_initial_time_;
928 if (active_time < kMemuseIntervals[memuse_interval_index_]) {
929 // Not enough active time has passed. Reschedule the callback.
930 ScheduleMemuseCallback(false, active_time);
931 } else {
932 // Enough active time has passed. Do the work, and (if we succeed) see if
933 // we need to do more.
934 if (MemuseCallbackWork() &&
935 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
936 memuse_interval_index_++;
937 ScheduleMemuseCallback(true, 0);
938 }
939 }
940}
941
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700942bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700943 string meminfo_raw;
944 const FilePath meminfo_path("/proc/meminfo");
945 if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
946 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
947 return false;
948 }
949 return ProcessMemuse(meminfo_raw);
950}
951
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700952bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700953 static const MeminfoRecord fields_array[] = {
954 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
955 { "ActiveAnon", "Active(anon)" },
956 { "InactiveAnon", "Inactive(anon)" },
957 };
958 vector<MeminfoRecord> fields(fields_array,
959 fields_array + arraysize(fields_array));
960 if (!FillMeminfo(meminfo_raw, &fields)) {
961 return false;
962 }
963 int total = fields[0].value;
964 int active_anon = fields[1].value;
965 int inactive_anon = fields[2].value;
966 if (total == 0) {
967 // this "cannot happen"
968 LOG(WARNING) << "borked meminfo parser";
969 return false;
970 }
971 string metrics_name = StringPrintf("Platform.MemuseAnon%d",
972 memuse_interval_index_);
973 SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total,
974 100, 101);
975 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700976}
977
Darin Petkovf1e85e42010-06-10 15:59:53 -0700978// static
Ken Mixterccd84c02010-08-16 19:57:13 -0700979void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
Darin Petkov1bb904e2010-06-16 15:58:06 -0700980 if (count <= 0)
981 return;
982
Darin Petkovf1e85e42010-06-10 15:59:53 -0700983 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle);
984 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute;
985 daemon->SendMetric(kMetricDailyUseTimeName, minutes,
986 kMetricDailyUseTimeMin,
987 kMetricDailyUseTimeMax,
988 kMetricDailyUseTimeBuckets);
989}
990
Darin Petkov38d5cb02010-06-24 12:10:26 -0700991void MetricsDaemon::SendMetric(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700992 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700993 DLOG(INFO) << "received metric: " << name << " " << sample << " "
994 << min << " " << max << " " << nbuckets;
995 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700996}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700997
998void MetricsDaemon::SendLinearMetric(const string& name, int sample,
999 int max, int nbuckets) {
1000 DLOG(INFO) << "received linear metric: " << name << " " << sample << " "
1001 << max << " " << nbuckets;
1002 // TODO(semenzato): add a proper linear histogram to the Chrome external
1003 // metrics API.
1004 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1005 metrics_lib_->SendEnumToUMA(name, sample, max);
1006}