blob: 069f68e25b7b51e3d677af9b00abdb744456e383 [file] [log] [blame]
Bertrand SIMONNET52e5b992015-08-10 15:18:00 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Darin Petkov65b01462010-04-14 13:32:20 -070016
Bertrand SIMONNET4b915ae2015-07-28 15:38:14 -070017#include "metrics_daemon.h"
Darin Petkov65b01462010-04-14 13:32:20 -070018
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080019#include <fcntl.h>
Luigi Semenzato4a6c9422014-06-30 18:12:28 -070020#include <inttypes.h>
Luigi Semenzato8accd332011-05-17 16:37:18 -070021#include <math.h>
Ken Mixter4c5daa42010-08-26 18:35:06 -070022#include <string.h>
Steve Funge86591e2014-12-01 13:38:21 -080023#include <sysexits.h>
Luigi Semenzato8accd332011-05-17 16:37:18 -070024#include <time.h>
Darin Petkov65b01462010-04-14 13:32:20 -070025
Bertrand SIMONNET4b915ae2015-07-28 15:38:14 -070026#include <base/bind.h>
Luigi Semenzato859b3f02014-02-05 15:33:19 -080027#include <base/files/file_path.h>
Ben Chan51bf92a2014-09-05 08:21:06 -070028#include <base/files/file_util.h>
Luigi Semenzato859b3f02014-02-05 15:33:19 -080029#include <base/hash.h>
Darin Petkov65b01462010-04-14 13:32:20 -070030#include <base/logging.h>
Ben Chan2e6543d2014-02-05 23:26:25 -080031#include <base/strings/string_number_conversions.h>
32#include <base/strings/string_split.h>
33#include <base/strings/string_util.h>
34#include <base/strings/stringprintf.h>
Luigi Semenzato859b3f02014-02-05 15:33:19 -080035#include <base/sys_info.h>
Steve Funge86591e2014-12-01 13:38:21 -080036#include <dbus/dbus.h>
37#include <dbus/message.h>
Bertrand SIMONNETbae5dcc2015-08-04 14:12:10 -070038
39#include "constants.h"
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070040#include "uploader/upload_service.h"
Darin Petkov65b01462010-04-14 13:32:20 -070041
Ben Chan2e6543d2014-02-05 23:26:25 -080042using base::FilePath;
43using base::StringPrintf;
Darin Petkovf27f0362010-06-04 13:14:19 -070044using base::Time;
45using base::TimeDelta;
46using base::TimeTicks;
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -080047using chromeos_metrics::PersistentInteger;
Luigi Semenzato8accd332011-05-17 16:37:18 -070048using std::map;
Darin Petkov38d5cb02010-06-24 12:10:26 -070049using std::string;
Luigi Semenzato8accd332011-05-17 16:37:18 -070050using std::vector;
51
Daniel Eratc83975a2014-04-04 08:53:44 -070052namespace {
Darin Petkovf27f0362010-06-04 13:14:19 -070053
Darin Petkov703ec972010-04-27 11:02:18 -070054#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
Darin Petkov40f25732013-04-29 15:07:31 +020055
Daniel Eratc83975a2014-04-04 08:53:44 -070056const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
57const char kCrashReporterUserCrashSignal[] = "UserCrash";
Steve Funge86591e2014-12-01 13:38:21 -080058const char kCrashReporterMatchRule[] =
59 "type='signal',interface='%s',path='/',member='%s'";
Darin Petkov41e06232010-05-03 16:45:37 -070060
Daniel Eratc83975a2014-04-04 08:53:44 -070061const int kSecondsPerMinute = 60;
62const int kMinutesPerHour = 60;
63const int kHoursPerDay = 24;
64const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
65const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
66const int kDaysPerWeek = 7;
67const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
Darin Petkov41e06232010-05-03 16:45:37 -070068
Daniel Eratc83975a2014-04-04 08:53:44 -070069// Interval between calls to UpdateStats().
Steve Funge86591e2014-12-01 13:38:21 -080070const uint32_t kUpdateStatsIntervalMs = 300000;
Darin Petkov65b01462010-04-14 13:32:20 -070071
Luigi Semenzatoc5a92342014-02-14 15:05:51 -080072const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
Daniel Eratc83975a2014-04-04 08:53:44 -070073const char kUncleanShutdownDetectedFile[] =
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -080074 "/var/run/unclean-shutdown-detected";
Ken Mixterccd84c02010-08-16 19:57:13 -070075
Daniel Eratc83975a2014-04-04 08:53:44 -070076} // namespace
77
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080078// disk stats metrics
79
80// The {Read,Write}Sectors numbers are in sectors/second.
81// A sector is usually 512 bytes.
82
83const char MetricsDaemon::kMetricReadSectorsLongName[] =
84 "Platform.ReadSectorsLong";
85const char MetricsDaemon::kMetricWriteSectorsLongName[] =
86 "Platform.WriteSectorsLong";
87const char MetricsDaemon::kMetricReadSectorsShortName[] =
88 "Platform.ReadSectorsShort";
89const char MetricsDaemon::kMetricWriteSectorsShortName[] =
90 "Platform.WriteSectorsShort";
91
Luigi Semenzato5bd764f2011-10-14 12:03:35 -070092const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
93const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080094
Luigi Semenzato29c7ef92011-04-12 14:12:35 -070095const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
96
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -080097// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
98// sectors.
99const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
100const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700101// Page size is 4k, sector size is 0.5k. We're not interested in page fault
102// rates that the disk cannot sustain.
103const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
104const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
105
106// Major page faults, i.e. the ones that require data to be read from disk.
107
108const char MetricsDaemon::kMetricPageFaultsLongName[] =
109 "Platform.PageFaultsLong";
110const char MetricsDaemon::kMetricPageFaultsShortName[] =
111 "Platform.PageFaultsShort";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800112
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700113// Swap in and Swap out
114
115const char MetricsDaemon::kMetricSwapInLongName[] =
116 "Platform.SwapInLong";
117const char MetricsDaemon::kMetricSwapInShortName[] =
118 "Platform.SwapInShort";
119
120const char MetricsDaemon::kMetricSwapOutLongName[] =
121 "Platform.SwapOutLong";
122const char MetricsDaemon::kMetricSwapOutShortName[] =
123 "Platform.SwapOutShort";
124
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700125const char MetricsDaemon::kMetricsProcStatFileName[] = "/proc/stat";
126const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11;
127
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700128// Thermal CPU throttling.
129
130const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
131 "Platform.CpuFrequencyThermalScaling";
132
Luigi Semenzato96360192014-06-04 10:53:35 -0700133// Zram sysfs entries.
134
135const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
136const char MetricsDaemon::kOrigDataSizeName[] = "orig_data_size";
137const char MetricsDaemon::kZeroPagesName[] = "zero_pages";
138
Luigi Semenzato8accd332011-05-17 16:37:18 -0700139// Memory use stats collection intervals. We collect some memory use interval
140// at these intervals after boot, and we stop collecting after the last one,
141// with the assumption that in most cases the memory use won't change much
142// after that.
143static const int kMemuseIntervals[] = {
144 1 * kSecondsPerMinute, // 1 minute mark
145 4 * kSecondsPerMinute, // 5 minute mark
146 25 * kSecondsPerMinute, // 0.5 hour mark
147 120 * kSecondsPerMinute, // 2.5 hour mark
148 600 * kSecondsPerMinute, // 12.5 hour mark
149};
150
Darin Petkovf1e85e42010-06-10 15:59:53 -0700151MetricsDaemon::MetricsDaemon()
Steve Funge86591e2014-12-01 13:38:21 -0800152 : memuse_final_time_(0),
Luigi Semenzato8accd332011-05-17 16:37:18 -0700153 memuse_interval_index_(0),
154 read_sectors_(0),
155 write_sectors_(0),
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700156 vmstats_(),
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700157 stats_state_(kStatsShort),
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700158 stats_initial_time_(0),
159 ticks_per_second_(0),
160 latest_cpu_use_ticks_(0) {}
Darin Petkovf1e85e42010-06-10 15:59:53 -0700161
Ken Mixter4c5daa42010-08-26 18:35:06 -0700162MetricsDaemon::~MetricsDaemon() {
Ken Mixter4c5daa42010-08-26 18:35:06 -0700163}
164
Luigi Semenzato8accd332011-05-17 16:37:18 -0700165double MetricsDaemon::GetActiveTime() {
166 struct timespec ts;
167 int r = clock_gettime(CLOCK_MONOTONIC, &ts);
168 if (r < 0) {
169 PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
170 return 0;
171 } else {
Luigi Semenzato4a6c9422014-06-30 18:12:28 -0700172 return ts.tv_sec + static_cast<double>(ts.tv_nsec) / (1000 * 1000 * 1000);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700173 }
174}
175
Steve Funge86591e2014-12-01 13:38:21 -0800176int MetricsDaemon::Run() {
Ken Mixterccd84c02010-08-16 19:57:13 -0700177 if (CheckSystemCrash(kKernelCrashDetectedFile)) {
178 ProcessKernelCrash();
179 }
180
181 if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
182 ProcessUncleanShutdown();
183 }
184
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800185 // On OS version change, clear version stats (which are reported daily).
Ben Chanf05ab402014-08-07 00:54:59 -0700186 int32_t version = GetOsVersionHash();
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800187 if (version_cycle_->Get() != version) {
188 version_cycle_->Set(version);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800189 kernel_crashes_version_count_->Set(0);
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -0700190 version_cumulative_active_use_->Set(0);
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700191 version_cumulative_cpu_use_->Set(0);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800192 }
193
Steve Funge86591e2014-12-01 13:38:21 -0800194 return chromeos::DBusDaemon::Run();
Darin Petkov65b01462010-04-14 13:32:20 -0700195}
196
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700197void MetricsDaemon::RunUploaderTest() {
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700198 upload_service_.reset(new UploadService(new SystemProfileCache(true,
199 config_root_),
Bertrand SIMONNETe4fa61e2015-02-18 09:38:55 -0800200 metrics_lib_,
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700201 server_));
Bertrand SIMONNETcac74e12014-10-09 10:14:13 -0700202 upload_service_->Init(upload_interval_, metrics_file_);
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700203 upload_service_->UploadEvent();
204}
205
Ben Chanf05ab402014-08-07 00:54:59 -0700206uint32_t MetricsDaemon::GetOsVersionHash() {
207 static uint32_t cached_version_hash = 0;
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800208 static bool version_hash_is_cached = false;
209 if (version_hash_is_cached)
210 return cached_version_hash;
211 version_hash_is_cached = true;
Bertrand SIMONNETbae5dcc2015-08-04 14:12:10 -0700212 std::string version = metrics::kDefaultVersion;
213 // The version might not be set for development devices. In this case, use the
214 // zero version.
215 base::SysInfo::GetLsbReleaseValue("BRILLO_VERSION", &version);
216 cached_version_hash = base::Hash(version);
217 if (testing_) {
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800218 cached_version_hash = 42; // return any plausible value for the hash
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800219 }
220 return cached_version_hash;
221}
222
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700223void MetricsDaemon::Init(bool testing,
224 bool uploader_active,
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700225 bool dbus_enabled,
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700226 MetricsLibraryInterface* metrics_lib,
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700227 const string& vmstats_path,
228 const string& scaling_max_freq_path,
Steve Fung67906c62014-10-06 15:15:30 -0700229 const string& cpuinfo_max_freq_path,
Bertrand SIMONNETcac74e12014-10-09 10:14:13 -0700230 const base::TimeDelta& upload_interval,
Steve Fung67906c62014-10-06 15:15:30 -0700231 const string& server,
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700232 const string& metrics_file,
233 const string& config_root) {
Darin Petkov65b01462010-04-14 13:32:20 -0700234 testing_ = testing;
Steve Funge86591e2014-12-01 13:38:21 -0800235 uploader_active_ = uploader_active;
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700236 dbus_enabled_ = dbus_enabled;
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700237 config_root_ = config_root;
Alex Vakulenko14595032014-08-28 14:59:56 -0700238 DCHECK(metrics_lib != nullptr);
Darin Petkovfc91b422010-05-12 13:05:45 -0700239 metrics_lib_ = metrics_lib;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700240
Bertrand SIMONNETcac74e12014-10-09 10:14:13 -0700241 upload_interval_ = upload_interval;
Steve Fung67906c62014-10-06 15:15:30 -0700242 server_ = server;
243 metrics_file_ = metrics_file;
244
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700245 // Get ticks per second (HZ) on this system.
246 // Sysconf cannot fail, so no sanity checks are needed.
247 ticks_per_second_ = sysconf(_SC_CLK_TCK);
248
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -0700249 daily_active_use_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700250 new PersistentInteger("Platform.DailyUseTime"));
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -0700251 version_cumulative_active_use_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700252 new PersistentInteger("Platform.CumulativeDailyUseTime"));
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700253 version_cumulative_cpu_use_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700254 new PersistentInteger("Platform.CumulativeCpuTime"));
Darin Petkov38d5cb02010-06-24 12:10:26 -0700255
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800256 kernel_crash_interval_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700257 new PersistentInteger("Platform.KernelCrashInterval"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800258 unclean_shutdown_interval_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700259 new PersistentInteger("Platform.UncleanShutdownInterval"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800260 user_crash_interval_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700261 new PersistentInteger("Platform.UserCrashInterval"));
Darin Petkov2ccef012010-05-05 16:06:37 -0700262
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800263 any_crashes_daily_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700264 new PersistentInteger("Platform.AnyCrashesDaily"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800265 any_crashes_weekly_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700266 new PersistentInteger("Platform.AnyCrashesWeekly"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800267 user_crashes_daily_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700268 new PersistentInteger("Platform.UserCrashesDaily"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800269 user_crashes_weekly_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700270 new PersistentInteger("Platform.UserCrashesWeekly"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800271 kernel_crashes_daily_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700272 new PersistentInteger("Platform.KernelCrashesDaily"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800273 kernel_crashes_weekly_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700274 new PersistentInteger("Platform.KernelCrashesWeekly"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800275 kernel_crashes_version_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700276 new PersistentInteger("Platform.KernelCrashesSinceUpdate"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800277 unclean_shutdowns_daily_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700278 new PersistentInteger("Platform.UncleanShutdownsDaily"));
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800279 unclean_shutdowns_weekly_count_.reset(
Luigi Semenzatodc865892015-07-09 08:28:08 -0700280 new PersistentInteger("Platform.UncleanShutdownsWeekly"));
Darin Petkov38d5cb02010-06-24 12:10:26 -0700281
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800282 daily_cycle_.reset(new PersistentInteger("daily.cycle"));
283 weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
284 version_cycle_.reset(new PersistentInteger("version.cycle"));
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800285
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700286 vmstats_path_ = vmstats_path;
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700287 scaling_max_freq_path_ = scaling_max_freq_path;
288 cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
Steve Funge86591e2014-12-01 13:38:21 -0800289}
290
291int MetricsDaemon::OnInit() {
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700292 int return_code = dbus_enabled_ ? chromeos::DBusDaemon::OnInit() :
293 chromeos::Daemon::OnInit();
Steve Funge86591e2014-12-01 13:38:21 -0800294 if (return_code != EX_OK)
295 return return_code;
296
Steve Funge86591e2014-12-01 13:38:21 -0800297 if (testing_)
298 return EX_OK;
Darin Petkov65b01462010-04-14 13:32:20 -0700299
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700300 if (dbus_enabled_) {
301 bus_->AssertOnDBusThread();
302 CHECK(bus_->SetUpAsyncOperations());
Darin Petkov65b01462010-04-14 13:32:20 -0700303
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700304 if (bus_->is_connected()) {
305 const std::string match_rule =
306 base::StringPrintf(kCrashReporterMatchRule,
307 kCrashReporterInterface,
308 kCrashReporterUserCrashSignal);
Darin Petkov65b01462010-04-14 13:32:20 -0700309
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700310 bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
Darin Petkov65b01462010-04-14 13:32:20 -0700311
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700312 DBusError error;
313 dbus_error_init(&error);
314 bus_->AddMatch(match_rule, &error);
Darin Petkov65b01462010-04-14 13:32:20 -0700315
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700316 if (dbus_error_is_set(&error)) {
317 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
318 << error.name << ": " << error.message;
319 return EX_SOFTWARE;
320 }
321 } else {
322 LOG(ERROR) << "DBus isn't connected.";
323 return EX_UNAVAILABLE;
Steve Funge86591e2014-12-01 13:38:21 -0800324 }
Darin Petkov703ec972010-04-27 11:02:18 -0700325 }
326
Steve Funge86591e2014-12-01 13:38:21 -0800327 if (uploader_active_) {
Bertrand SIMONNETbae5dcc2015-08-04 14:12:10 -0700328 upload_service_.reset(
329 new UploadService(new SystemProfileCache(), metrics_lib_, server_));
330 upload_service_->Init(upload_interval_, metrics_file_);
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700331 }
Steve Funge86591e2014-12-01 13:38:21 -0800332
333 return EX_OK;
Darin Petkov65b01462010-04-14 13:32:20 -0700334}
335
Steve Funge86591e2014-12-01 13:38:21 -0800336void MetricsDaemon::OnShutdown(int* return_code) {
Bertrand SIMONNETfec4d2c2015-08-05 16:04:14 -0700337 if (!testing_ && dbus_enabled_ && bus_->is_connected()) {
Steve Funge86591e2014-12-01 13:38:21 -0800338 const std::string match_rule =
339 base::StringPrintf(kCrashReporterMatchRule,
340 kCrashReporterInterface,
341 kCrashReporterUserCrashSignal);
342
343 bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
344
345 DBusError error;
346 dbus_error_init(&error);
347 bus_->RemoveMatch(match_rule, &error);
348
349 if (dbus_error_is_set(&error)) {
350 LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got "
351 << error.name << ": " << error.message;
352 }
353 }
354 chromeos::DBusDaemon::OnShutdown(return_code);
Darin Petkov65b01462010-04-14 13:32:20 -0700355}
356
Darin Petkov703ec972010-04-27 11:02:18 -0700357// static
358DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
359 DBusMessage* message,
360 void* user_data) {
Darin Petkov703ec972010-04-27 11:02:18 -0700361 int message_type = dbus_message_get_type(message);
362 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
Darin Petkov41e06232010-05-03 16:45:37 -0700363 DLOG(WARNING) << "unexpected message type " << message_type;
Darin Petkov703ec972010-04-27 11:02:18 -0700364 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
365 }
366
367 // Signal messages always have interfaces.
Daniel Eratc83975a2014-04-04 08:53:44 -0700368 const std::string interface(dbus_message_get_interface(message));
369 const std::string member(dbus_message_get_member(message));
370 DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal";
Darin Petkov703ec972010-04-27 11:02:18 -0700371
372 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
373
374 DBusMessageIter iter;
375 dbus_message_iter_init(message, &iter);
Daniel Eratc83975a2014-04-04 08:53:44 -0700376 if (interface == kCrashReporterInterface) {
377 CHECK_EQ(member, kCrashReporterUserCrashSignal);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700378 daemon->ProcessUserCrash();
Darin Petkov703ec972010-04-27 11:02:18 -0700379 } else {
Daniel Eratc83975a2014-04-04 08:53:44 -0700380 // Ignore messages from the bus itself.
Darin Petkov703ec972010-04-27 11:02:18 -0700381 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
382 }
383
384 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700385}
386
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700387// One might argue that parts of this should go into
388// chromium/src/base/sys_info_chromeos.c instead, but put it here for now.
389
390TimeDelta MetricsDaemon::GetIncrementalCpuUse() {
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700391 FilePath proc_stat_path = FilePath(kMetricsProcStatFileName);
392 std::string proc_stat_string;
393 if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
394 LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
395 return TimeDelta();
396 }
397
398 std::vector<std::string> proc_stat_lines;
399 base::SplitString(proc_stat_string, '\n', &proc_stat_lines);
400 if (proc_stat_lines.empty()) {
401 LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName
402 << ": " << proc_stat_string;
403 return TimeDelta();
404 }
405 std::vector<std::string> proc_stat_totals;
406 base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
407
Ben Chanf05ab402014-08-07 00:54:59 -0700408 uint64_t user_ticks, user_nice_ticks, system_ticks;
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700409 if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
410 proc_stat_totals[0] != "cpu" ||
411 !base::StringToUint64(proc_stat_totals[1], &user_ticks) ||
412 !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) ||
413 !base::StringToUint64(proc_stat_totals[3], &system_ticks)) {
414 LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
415 return TimeDelta(base::TimeDelta::FromSeconds(0));
416 }
417
Ben Chanf05ab402014-08-07 00:54:59 -0700418 uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks;
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700419
420 // Sanity check.
421 if (total_cpu_use_ticks < latest_cpu_use_ticks_) {
422 LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_
423 << " to " << total_cpu_use_ticks;
424 return TimeDelta();
425 }
426
Ben Chanf05ab402014-08-07 00:54:59 -0700427 uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_;
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -0700428 latest_cpu_use_ticks_ = total_cpu_use_ticks;
429 // Use microseconds to avoid significant truncations.
430 return base::TimeDelta::FromMicroseconds(
431 diff * 1000 * 1000 / ticks_per_second_);
432}
433
Darin Petkov1bb904e2010-06-16 15:58:06 -0700434void MetricsDaemon::ProcessUserCrash() {
Daniel Eratc83975a2014-04-04 08:53:44 -0700435 // Counts the active time up to now.
436 UpdateStats(TimeTicks::Now(), Time::Now());
Darin Petkov1bb904e2010-06-16 15:58:06 -0700437
438 // Reports the active use time since the last crash and resets it.
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800439 SendCrashIntervalSample(user_crash_interval_);
Ken Mixterccd84c02010-08-16 19:57:13 -0700440
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800441 any_crashes_daily_count_->Add(1);
442 any_crashes_weekly_count_->Add(1);
443 user_crashes_daily_count_->Add(1);
444 user_crashes_weekly_count_->Add(1);
Darin Petkov1bb904e2010-06-16 15:58:06 -0700445}
446
Darin Petkov38d5cb02010-06-24 12:10:26 -0700447void MetricsDaemon::ProcessKernelCrash() {
Daniel Eratc83975a2014-04-04 08:53:44 -0700448 // Counts the active time up to now.
449 UpdateStats(TimeTicks::Now(), Time::Now());
Darin Petkov38d5cb02010-06-24 12:10:26 -0700450
451 // Reports the active use time since the last crash and resets it.
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800452 SendCrashIntervalSample(kernel_crash_interval_);
Ken Mixterccd84c02010-08-16 19:57:13 -0700453
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800454 any_crashes_daily_count_->Add(1);
455 any_crashes_weekly_count_->Add(1);
456 kernel_crashes_daily_count_->Add(1);
457 kernel_crashes_weekly_count_->Add(1);
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800458
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800459 kernel_crashes_version_count_->Add(1);
Darin Petkov38d5cb02010-06-24 12:10:26 -0700460}
461
Ken Mixterccd84c02010-08-16 19:57:13 -0700462void MetricsDaemon::ProcessUncleanShutdown() {
Daniel Eratc83975a2014-04-04 08:53:44 -0700463 // Counts the active time up to now.
464 UpdateStats(TimeTicks::Now(), Time::Now());
Ken Mixterccd84c02010-08-16 19:57:13 -0700465
466 // Reports the active use time since the last crash and resets it.
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800467 SendCrashIntervalSample(unclean_shutdown_interval_);
Ken Mixterccd84c02010-08-16 19:57:13 -0700468
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800469 unclean_shutdowns_daily_count_->Add(1);
470 unclean_shutdowns_weekly_count_->Add(1);
471 any_crashes_daily_count_->Add(1);
472 any_crashes_weekly_count_->Add(1);
Ken Mixterccd84c02010-08-16 19:57:13 -0700473}
474
Luigi Semenzato8accd332011-05-17 16:37:18 -0700475bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
Darin Petkov38d5cb02010-06-24 12:10:26 -0700476 FilePath crash_detected(crash_file);
Ben Chan2e6543d2014-02-05 23:26:25 -0800477 if (!base::PathExists(crash_detected))
Ken Mixterccd84c02010-08-16 19:57:13 -0700478 return false;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700479
480 // Deletes the crash-detected file so that the daemon doesn't report
481 // another kernel crash in case it's restarted.
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800482 base::DeleteFile(crash_detected, false); // not recursive
Ken Mixterccd84c02010-08-16 19:57:13 -0700483 return true;
Darin Petkov38d5cb02010-06-24 12:10:26 -0700484}
485
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700486void MetricsDaemon::StatsReporterInit() {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800487 DiskStatsReadStats(&read_sectors_, &write_sectors_);
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700488 VmStatsReadStats(&vmstats_);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800489 // The first time around just run the long stat, so we don't delay boot.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700490 stats_state_ = kStatsLong;
491 stats_initial_time_ = GetActiveTime();
492 if (stats_initial_time_ < 0) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700493 LOG(WARNING) << "not collecting disk stats";
494 } else {
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700495 ScheduleStatsCallback(kMetricStatsLongInterval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700496 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800497}
498
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700499void MetricsDaemon::ScheduleStatsCallback(int wait) {
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800500 if (testing_) {
501 return;
502 }
Steve Funge86591e2014-12-01 13:38:21 -0800503 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
504 base::Bind(&MetricsDaemon::StatsCallback, base::Unretained(this)),
505 base::TimeDelta::FromSeconds(wait));
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800506}
507
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700508bool MetricsDaemon::VmStatsParseStats(const char* stats,
509 struct VmstatRecord* record) {
510 // a mapping of string name to field in VmstatRecord and whether we found it
511 struct mapping {
512 const string name;
513 uint64_t* value_p;
514 bool found;
515 } map[] =
516 { { .name = "pgmajfault",
517 .value_p = &record->page_faults_,
518 .found = false },
519 { .name = "pswpin",
520 .value_p = &record->swap_in_,
521 .found = false },
522 { .name = "pswpout",
523 .value_p = &record->swap_out_,
524 .found = false }, };
525
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700526 // Each line in the file has the form
527 // <ID> <VALUE>
528 // for instance:
529 // nr_free_pages 213427
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700530 vector<string> lines;
531 Tokenize(stats, "\n", &lines);
532 for (vector<string>::iterator it = lines.begin();
533 it != lines.end(); ++it) {
534 vector<string> tokens;
535 base::SplitString(*it, ' ', &tokens);
536 if (tokens.size() == 2) {
537 for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
538 if (!tokens[0].compare(map[i].name)) {
539 if (!base::StringToUint64(tokens[1], map[i].value_p))
540 return false;
541 map[i].found = true;
542 }
543 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700544 } else {
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700545 LOG(WARNING) << "unexpected vmstat format";
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700546 }
547 }
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700548 // make sure we got all the stats
549 for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
550 if (map[i].found == false) {
551 LOG(WARNING) << "vmstat missing " << map[i].name;
552 return false;
553 }
554 }
555 return true;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700556}
557
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700558bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
559 string value_string;
560 FilePath* path = new FilePath(vmstats_path_);
Ben Chan2e6543d2014-02-05 23:26:25 -0800561 if (!base::ReadFileToString(*path, &value_string)) {
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700562 delete path;
563 LOG(WARNING) << "cannot read " << vmstats_path_;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700564 return false;
565 }
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700566 delete path;
567 return VmStatsParseStats(value_string.c_str(), stats);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800568}
569
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700570bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
Luigi Semenzatod92d18c2013-06-04 13:24:21 -0700571 const FilePath sysfs_path(sysfs_file_name);
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700572 string value_string;
Ben Chan2e6543d2014-02-05 23:26:25 -0800573 if (!base::ReadFileToString(sysfs_path, &value_string)) {
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700574 LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
575 return false;
576 }
Ben Chan2e6543d2014-02-05 23:26:25 -0800577 if (!base::RemoveChars(value_string, "\n", &value_string)) {
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700578 LOG(WARNING) << "no newline in " << value_string;
579 // Continue even though the lack of newline is suspicious.
580 }
581 if (!base::StringToInt(value_string, value)) {
582 LOG(WARNING) << "cannot convert " << value_string << " to int";
583 return false;
584 }
585 return true;
586}
587
588void MetricsDaemon::SendCpuThrottleMetrics() {
589 // |max_freq| is 0 only the first time through.
590 static int max_freq = 0;
591 if (max_freq == -1)
592 // Give up, as sysfs did not report max_freq correctly.
593 return;
594 if (max_freq == 0 || testing_) {
595 // One-time initialization of max_freq. (Every time when testing.)
596 if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
597 max_freq = -1;
598 return;
599 }
600 if (max_freq == 0) {
601 LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
602 max_freq = -1;
603 return;
604 }
605 if (max_freq % 10000 == 1000) {
606 // Special case: system has turbo mode, and max non-turbo frequency is
607 // max_freq - 1000. This relies on "normal" (non-turbo) frequencies
608 // being multiples of (at least) 10 MHz. Although there is no guarantee
609 // of this, it seems a fairly reasonable assumption. Otherwise we should
610 // read scaling_available_frequencies, sort the frequencies, compare the
611 // two highest ones, and check if they differ by 1000 (kHz) (and that's a
612 // hack too, no telling when it will change).
613 max_freq -= 1000;
614 }
615 }
616 int scaled_freq = 0;
617 if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
618 return;
619 // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but
620 // scaled_freq is not the actual turbo frequency. We indicate this situation
621 // with a 101% value.
622 int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800623 SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700624}
625
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700626// Collects disk and vm stats alternating over a short and a long interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700627
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700628void MetricsDaemon::StatsCallback() {
Ben Chanf05ab402014-08-07 00:54:59 -0700629 uint64_t read_sectors_now, write_sectors_now;
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700630 struct VmstatRecord vmstats_now;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700631 double time_now = GetActiveTime();
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700632 double delta_time = time_now - stats_initial_time_;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700633 if (testing_) {
634 // Fake the time when testing.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700635 delta_time = stats_state_ == kStatsShort ?
636 kMetricStatsShortInterval : kMetricStatsLongInterval;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700637 }
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700638 bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
639 &write_sectors_now);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700640 int delta_read = read_sectors_now - read_sectors_;
641 int delta_write = write_sectors_now - write_sectors_;
642 int read_sectors_per_second = delta_read / delta_time;
643 int write_sectors_per_second = delta_write / delta_time;
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700644 bool vmstats_success = VmStatsReadStats(&vmstats_now);
645 uint64_t delta_faults = vmstats_now.page_faults_ - vmstats_.page_faults_;
646 uint64_t delta_swap_in = vmstats_now.swap_in_ - vmstats_.swap_in_;
647 uint64_t delta_swap_out = vmstats_now.swap_out_ - vmstats_.swap_out_;
648 uint64_t page_faults_per_second = delta_faults / delta_time;
649 uint64_t swap_in_per_second = delta_swap_in / delta_time;
650 uint64_t swap_out_per_second = delta_swap_out / delta_time;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800651
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700652 switch (stats_state_) {
653 case kStatsShort:
654 if (diskstats_success) {
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800655 SendSample(kMetricReadSectorsShortName,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700656 read_sectors_per_second,
657 1,
658 kMetricSectorsIOMax,
659 kMetricSectorsBuckets);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800660 SendSample(kMetricWriteSectorsShortName,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700661 write_sectors_per_second,
662 1,
663 kMetricSectorsIOMax,
664 kMetricSectorsBuckets);
665 }
666 if (vmstats_success) {
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800667 SendSample(kMetricPageFaultsShortName,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700668 page_faults_per_second,
669 1,
670 kMetricPageFaultsMax,
671 kMetricPageFaultsBuckets);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800672 SendSample(kMetricSwapInShortName,
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700673 swap_in_per_second,
674 1,
675 kMetricPageFaultsMax,
676 kMetricPageFaultsBuckets);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800677 SendSample(kMetricSwapOutShortName,
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700678 swap_out_per_second,
679 1,
680 kMetricPageFaultsMax,
681 kMetricPageFaultsBuckets);
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700682 }
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800683 // Schedule long callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700684 stats_state_ = kStatsLong;
685 ScheduleStatsCallback(kMetricStatsLongInterval -
686 kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800687 break;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700688 case kStatsLong:
689 if (diskstats_success) {
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800690 SendSample(kMetricReadSectorsLongName,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700691 read_sectors_per_second,
692 1,
693 kMetricSectorsIOMax,
694 kMetricSectorsBuckets);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800695 SendSample(kMetricWriteSectorsLongName,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700696 write_sectors_per_second,
697 1,
698 kMetricSectorsIOMax,
699 kMetricSectorsBuckets);
700 // Reset sector counters.
701 read_sectors_ = read_sectors_now;
702 write_sectors_ = write_sectors_now;
703 }
704 if (vmstats_success) {
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800705 SendSample(kMetricPageFaultsLongName,
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700706 page_faults_per_second,
707 1,
708 kMetricPageFaultsMax,
709 kMetricPageFaultsBuckets);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800710 SendSample(kMetricSwapInLongName,
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700711 swap_in_per_second,
712 1,
713 kMetricPageFaultsMax,
714 kMetricPageFaultsBuckets);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800715 SendSample(kMetricSwapOutLongName,
Sonny Rao4b8aebb2013-07-31 23:18:31 -0700716 swap_out_per_second,
717 1,
718 kMetricPageFaultsMax,
719 kMetricPageFaultsBuckets);
720
721 vmstats_ = vmstats_now;
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700722 }
Luigi Semenzatofb3a8212013-05-07 16:55:00 -0700723 SendCpuThrottleMetrics();
Luigi Semenzato8accd332011-05-17 16:37:18 -0700724 // Set start time for new cycle.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700725 stats_initial_time_ = time_now;
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800726 // Schedule short callback.
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700727 stats_state_ = kStatsShort;
728 ScheduleStatsCallback(kMetricStatsShortInterval);
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800729 break;
730 default:
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700731 LOG(FATAL) << "Invalid stats state";
Luigi Semenzatoc88e42d2011-02-17 10:21:16 -0800732 }
733}
734
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700735void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
736 if (testing_) {
737 return;
738 }
Steve Funge86591e2014-12-01 13:38:21 -0800739 base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
740 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
741 base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
Steve Fung8ab89c52015-01-05 13:48:30 -0800742 waitDelta),
Steve Funge86591e2014-12-01 13:38:21 -0800743 waitDelta);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700744}
745
Steve Funge86591e2014-12-01 13:38:21 -0800746void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700747 string meminfo_raw;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700748 const FilePath meminfo_path("/proc/meminfo");
Ben Chan2e6543d2014-02-05 23:26:25 -0800749 if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700750 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
Steve Funge86591e2014-12-01 13:38:21 -0800751 return;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700752 }
Luigi Semenzato96360192014-06-04 10:53:35 -0700753 // Make both calls even if the first one fails.
754 bool success = ProcessMeminfo(meminfo_raw);
Steve Funge86591e2014-12-01 13:38:21 -0800755 bool reschedule =
756 ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) &&
Luigi Semenzato96360192014-06-04 10:53:35 -0700757 success;
Steve Funge86591e2014-12-01 13:38:21 -0800758 if (reschedule) {
759 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
760 base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
Steve Fung8ab89c52015-01-05 13:48:30 -0800761 wait),
Steve Funge86591e2014-12-01 13:38:21 -0800762 wait);
763 }
Luigi Semenzato96360192014-06-04 10:53:35 -0700764}
765
766// static
767bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path,
Ben Chanf05ab402014-08-07 00:54:59 -0700768 uint64_t* value) {
Luigi Semenzato96360192014-06-04 10:53:35 -0700769 std::string content;
770 if (!base::ReadFileToString(path, &content)) {
771 PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
772 return false;
773 }
Luigi Semenzato4a6c9422014-06-30 18:12:28 -0700774 // Remove final newline.
775 base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
Luigi Semenzato96360192014-06-04 10:53:35 -0700776 if (!base::StringToUint64(content, value)) {
777 LOG(WARNING) << "invalid integer: " << content;
778 return false;
779 }
780 return true;
781}
782
783bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) {
784 // Data sizes are in bytes. |zero_pages| is in number of pages.
Ben Chanf05ab402014-08-07 00:54:59 -0700785 uint64_t compr_data_size, orig_data_size, zero_pages;
Luigi Semenzato96360192014-06-04 10:53:35 -0700786 const size_t page_size = 4096;
787
788 if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName),
789 &compr_data_size) ||
790 !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) ||
791 !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) {
792 return false;
793 }
794
795 // |orig_data_size| does not include zero-filled pages.
796 orig_data_size += zero_pages * page_size;
797
798 const int compr_data_size_mb = compr_data_size >> 20;
799 const int savings_mb = (orig_data_size - compr_data_size) >> 20;
800 const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size;
801
802 // Report compressed size in megabytes. 100 MB or less has little impact.
803 SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50);
804 SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50);
805 // The compression ratio is multiplied by 100 for better resolution. The
806 // ratios of interest are between 1 and 6 (100% and 600% as reported). We
807 // don't want samples when very little memory is being compressed.
808 if (compr_data_size_mb >= 1) {
809 SendSample("Platform.ZramCompressionRatioPercent",
810 orig_data_size * 100 / compr_data_size, 100, 600, 50);
811 }
812 // The values of interest for zero_pages are between 1MB and 1GB. The units
813 // are number of pages.
814 SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50);
815 SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50);
816
817 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700818}
819
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700820bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700821 static const MeminfoRecord fields_array[] = {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700822 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
823 { "MemFree", "MemFree" },
824 { "Buffers", "Buffers" },
825 { "Cached", "Cached" },
826 // { "SwapCached", "SwapCached" },
827 { "Active", "Active" },
828 { "Inactive", "Inactive" },
829 { "ActiveAnon", "Active(anon)" },
830 { "InactiveAnon", "Inactive(anon)" },
831 { "ActiveFile" , "Active(file)" },
832 { "InactiveFile", "Inactive(file)" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800833 { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700834 // { "Mlocked", "Mlocked" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800835 { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
836 { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700837 // { "Dirty", "Dirty" },
838 // { "Writeback", "Writeback" },
839 { "AnonPages", "AnonPages" },
840 { "Mapped", "Mapped" },
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800841 { "Shmem", "Shmem", kMeminfoOp_HistLog },
842 { "Slab", "Slab", kMeminfoOp_HistLog },
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700843 // { "SReclaimable", "SReclaimable" },
844 // { "SUnreclaim", "SUnreclaim" },
845 };
Luigi Semenzato8accd332011-05-17 16:37:18 -0700846 vector<MeminfoRecord> fields(fields_array,
847 fields_array + arraysize(fields_array));
848 if (!FillMeminfo(meminfo_raw, &fields)) {
849 return false;
850 }
851 int total_memory = fields[0].value;
852 if (total_memory == 0) {
853 // this "cannot happen"
854 LOG(WARNING) << "borked meminfo parser";
855 return false;
856 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800857 int swap_total = 0;
858 int swap_free = 0;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700859 // Send all fields retrieved, except total memory.
860 for (unsigned int i = 1; i < fields.size(); i++) {
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800861 string metrics_name = base::StringPrintf("Platform.Meminfo%s",
862 fields[i].name);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800863 int percent;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800864 switch (fields[i].op) {
865 case kMeminfoOp_HistPercent:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800866 // report value as percent of total memory
867 percent = fields[i].value * 100 / total_memory;
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800868 SendLinearSample(metrics_name, percent, 100, 101);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800869 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800870 case kMeminfoOp_HistLog:
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800871 // report value in kbytes, log scale, 4Gb max
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800872 SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800873 break;
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800874 case kMeminfoOp_SwapTotal:
875 swap_total = fields[i].value;
876 case kMeminfoOp_SwapFree:
877 swap_free = fields[i].value;
Luigi Semenzato3ccca062013-02-04 19:50:45 -0800878 break;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700879 }
880 }
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800881 if (swap_total > 0) {
882 int swap_used = swap_total - swap_free;
883 int swap_used_percent = swap_used * 100 / swap_total;
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800884 SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
885 SendLinearSample("Platform.MeminfoSwapUsedPercent", swap_used_percent,
Luigi Semenzato942cbab2013-02-12 13:17:07 -0800886 100, 101);
887 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700888 return true;
889}
890
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700891bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
892 vector<MeminfoRecord>* fields) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700893 vector<string> lines;
894 unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700895
896 // Scan meminfo output and collect field values. Each field name has to
897 // match a meminfo entry (case insensitive) after removing non-alpha
898 // characters from the entry.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700899 unsigned int ifield = 0;
900 for (unsigned int iline = 0;
901 iline < nlines && ifield < fields->size();
902 iline++) {
903 vector<string> tokens;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700904 Tokenize(lines[iline], ": ", &tokens);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700905 if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
906 // Name matches. Parse value and save.
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700907 char* rest;
Luigi Semenzato8accd332011-05-17 16:37:18 -0700908 (*fields)[ifield].value =
909 static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700910 if (*rest != '\0') {
911 LOG(WARNING) << "missing meminfo value";
912 return false;
913 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700914 ifield++;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700915 }
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700916 }
Luigi Semenzato8accd332011-05-17 16:37:18 -0700917 if (ifield < fields->size()) {
918 // End of input reached while scanning.
919 LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
920 << " and following";
921 return false;
922 }
923 return true;
924}
925
Luigi Semenzato0d9a9c92013-12-05 15:55:12 -0800926void MetricsDaemon::ScheduleMemuseCallback(double interval) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700927 if (testing_) {
928 return;
929 }
Steve Funge86591e2014-12-01 13:38:21 -0800930 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
931 base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
932 base::TimeDelta::FromSeconds(interval));
Luigi Semenzato8accd332011-05-17 16:37:18 -0700933}
934
935void MetricsDaemon::MemuseCallback() {
936 // Since we only care about active time (i.e. uptime minus sleep time) but
937 // the callbacks are driven by real time (uptime), we check if we should
938 // reschedule this callback due to intervening sleep periods.
939 double now = GetActiveTime();
Luigi Semenzato0d9a9c92013-12-05 15:55:12 -0800940 // Avoid intervals of less than one second.
941 double remaining_time = ceil(memuse_final_time_ - now);
942 if (remaining_time > 0) {
943 ScheduleMemuseCallback(remaining_time);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700944 } else {
Luigi Semenzato0d9a9c92013-12-05 15:55:12 -0800945 // Report stats and advance the measurement interval unless there are
946 // errors or we've completed the last interval.
Luigi Semenzato8accd332011-05-17 16:37:18 -0700947 if (MemuseCallbackWork() &&
Luigi Semenzato0d9a9c92013-12-05 15:55:12 -0800948 memuse_interval_index_ < arraysize(kMemuseIntervals)) {
949 double interval = kMemuseIntervals[memuse_interval_index_++];
950 memuse_final_time_ = now + interval;
951 ScheduleMemuseCallback(interval);
Luigi Semenzato8accd332011-05-17 16:37:18 -0700952 }
953 }
954}
955
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700956bool MetricsDaemon::MemuseCallbackWork() {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700957 string meminfo_raw;
958 const FilePath meminfo_path("/proc/meminfo");
Ben Chan2e6543d2014-02-05 23:26:25 -0800959 if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700960 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
961 return false;
962 }
963 return ProcessMemuse(meminfo_raw);
964}
965
Luigi Semenzato5bd764f2011-10-14 12:03:35 -0700966bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
Luigi Semenzato8accd332011-05-17 16:37:18 -0700967 static const MeminfoRecord fields_array[] = {
968 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
969 { "ActiveAnon", "Active(anon)" },
970 { "InactiveAnon", "Inactive(anon)" },
971 };
972 vector<MeminfoRecord> fields(fields_array,
973 fields_array + arraysize(fields_array));
974 if (!FillMeminfo(meminfo_raw, &fields)) {
975 return false;
976 }
977 int total = fields[0].value;
978 int active_anon = fields[1].value;
979 int inactive_anon = fields[2].value;
980 if (total == 0) {
981 // this "cannot happen"
982 LOG(WARNING) << "borked meminfo parser";
983 return false;
984 }
Luigi Semenzato859b3f02014-02-05 15:33:19 -0800985 string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
986 memuse_interval_index_);
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800987 SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
Luigi Semenzato8accd332011-05-17 16:37:18 -0700988 100, 101);
989 return true;
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700990}
991
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800992void MetricsDaemon::SendSample(const string& name, int sample,
Darin Petkov11b8eb32010-05-18 11:00:59 -0700993 int min, int max, int nbuckets) {
Darin Petkovfc91b422010-05-12 13:05:45 -0700994 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
Darin Petkov65b01462010-04-14 13:32:20 -0700995}
Luigi Semenzato29c7ef92011-04-12 14:12:35 -0700996
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -0700997void MetricsDaemon::SendKernelCrashesCumulativeCountStats() {
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -0800998 // Report the number of crashes for this OS version, but don't clear the
999 // counter. It is cleared elsewhere on version change.
Ben Chanf05ab402014-08-07 00:54:59 -07001000 int64_t crashes_count = kernel_crashes_version_count_->Get();
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -08001001 SendSample(kernel_crashes_version_count_->Name(),
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -07001002 crashes_count,
1003 1, // value of first bucket
1004 500, // value of last bucket
1005 100); // number of buckets
1006
1007
Ben Chanf05ab402014-08-07 00:54:59 -07001008 int64_t cpu_use_ms = version_cumulative_cpu_use_->Get();
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -07001009 SendSample(version_cumulative_cpu_use_->Name(),
1010 cpu_use_ms / 1000, // stat is in seconds
1011 1, // device may be used very little...
1012 8 * 1000 * 1000, // ... or a lot (a little over 90 days)
1013 100);
1014
1015 // On the first run after an autoupdate, cpu_use_ms and active_use_seconds
1016 // can be zero. Avoid division by zero.
1017 if (cpu_use_ms > 0) {
1018 // Send the crash frequency since update in number of crashes per CPU year.
1019 SendSample("Logging.KernelCrashesPerCpuYear",
1020 crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms,
1021 1,
1022 1000 * 1000, // about one crash every 30s of CPU time
1023 100);
1024 }
1025
Ben Chanf05ab402014-08-07 00:54:59 -07001026 int64_t active_use_seconds = version_cumulative_active_use_->Get();
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -07001027 if (active_use_seconds > 0) {
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -07001028 SendSample(version_cumulative_active_use_->Name(),
1029 active_use_seconds / 1000, // stat is in seconds
1030 1, // device may be used very little...
1031 8 * 1000 * 1000, // ... or a lot (about 90 days)
1032 100);
Luigi Semenzatoba0c65d2014-03-17 12:28:38 -07001033 // Same as above, but per year of active time.
1034 SendSample("Logging.KernelCrashesPerActiveYear",
1035 crashes_count * kSecondsPerDay * 365 / active_use_seconds,
1036 1,
1037 1000 * 1000, // about one crash every 30s of active time
1038 100);
1039 }
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -08001040}
1041
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -07001042void MetricsDaemon::SendDailyUseSample(
1043 const scoped_ptr<PersistentInteger>& use) {
1044 SendSample(use->Name(),
1045 use->GetAndClear(),
1046 1, // value of first bucket
1047 kSecondsPerDay, // value of last bucket
1048 50); // number of buckets
1049}
1050
Luigi Semenzato2fd51cc2014-02-26 11:53:16 -08001051void MetricsDaemon::SendCrashIntervalSample(
1052 const scoped_ptr<PersistentInteger>& interval) {
1053 SendSample(interval->Name(),
1054 interval->GetAndClear(),
1055 1, // value of first bucket
1056 4 * kSecondsPerWeek, // value of last bucket
1057 50); // number of buckets
1058}
1059
1060void MetricsDaemon::SendCrashFrequencySample(
1061 const scoped_ptr<PersistentInteger>& frequency) {
1062 SendSample(frequency->Name(),
1063 frequency->GetAndClear(),
1064 1, // value of first bucket
1065 100, // value of last bucket
1066 50); // number of buckets
1067}
1068
1069void MetricsDaemon::SendLinearSample(const string& name, int sample,
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001070 int max, int nbuckets) {
Luigi Semenzato29c7ef92011-04-12 14:12:35 -07001071 // TODO(semenzato): add a proper linear histogram to the Chrome external
1072 // metrics API.
1073 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
1074 metrics_lib_->SendEnumToUMA(name, sample, max);
1075}
Daniel Eratc83975a2014-04-04 08:53:44 -07001076
1077void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
1078 Time now_wall_time) {
1079 const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds();
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -07001080 daily_active_use_->Add(elapsed_seconds);
1081 version_cumulative_active_use_->Add(elapsed_seconds);
Daniel Eratc83975a2014-04-04 08:53:44 -07001082 user_crash_interval_->Add(elapsed_seconds);
1083 kernel_crash_interval_->Add(elapsed_seconds);
1084 version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds());
1085 last_update_stats_time_ = now_ticks;
1086
1087 const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
1088 const int day = since_epoch.InDays();
1089 const int week = day / 7;
1090
1091 if (daily_cycle_->Get() != day) {
1092 daily_cycle_->Set(day);
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -07001093 SendDailyUseSample(daily_active_use_);
1094 SendDailyUseSample(version_cumulative_active_use_);
Daniel Eratc83975a2014-04-04 08:53:44 -07001095 SendCrashFrequencySample(any_crashes_daily_count_);
1096 SendCrashFrequencySample(user_crashes_daily_count_);
1097 SendCrashFrequencySample(kernel_crashes_daily_count_);
1098 SendCrashFrequencySample(unclean_shutdowns_daily_count_);
Luigi Semenzatoe5883fa2014-04-18 17:00:35 -07001099 SendKernelCrashesCumulativeCountStats();
Daniel Eratc83975a2014-04-04 08:53:44 -07001100 }
1101
1102 if (weekly_cycle_->Get() != week) {
1103 weekly_cycle_->Set(week);
1104 SendCrashFrequencySample(any_crashes_weekly_count_);
1105 SendCrashFrequencySample(user_crashes_weekly_count_);
1106 SendCrashFrequencySample(kernel_crashes_weekly_count_);
1107 SendCrashFrequencySample(unclean_shutdowns_weekly_count_);
1108 }
1109}
1110
Steve Funge86591e2014-12-01 13:38:21 -08001111void MetricsDaemon::HandleUpdateStatsTimeout() {
1112 UpdateStats(TimeTicks::Now(), Time::Now());
1113 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
1114 base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
1115 base::Unretained(this)),
1116 base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
Daniel Eratc83975a2014-04-04 08:53:44 -07001117}