OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "metrics_daemon.h" | 5 #include "metrics_daemon.h" |
6 | 6 |
7 #include <fcntl.h> | |
7 #include <string.h> | 8 #include <string.h> |
8 | 9 |
9 #include <base/file_util.h> | 10 #include <base/file_util.h> |
10 #include <base/logging.h> | 11 #include <base/logging.h> |
11 #include <dbus/dbus-glib-lowlevel.h> | 12 #include <dbus/dbus-glib-lowlevel.h> |
12 | 13 |
13 #include "counter.h" | 14 #include "counter.h" |
14 | 15 |
15 using base::Time; | 16 using base::Time; |
16 using base::TimeDelta; | 17 using base::TimeDelta; |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
77 const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] = | 78 const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] = |
78 "Logging.UncleanShutdownsWeekly"; | 79 "Logging.UncleanShutdownsWeekly"; |
79 const char MetricsDaemon::kMetricUserCrashesDailyName[] = | 80 const char MetricsDaemon::kMetricUserCrashesDailyName[] = |
80 "Logging.UserCrashesDaily"; | 81 "Logging.UserCrashesDaily"; |
81 const char MetricsDaemon::kMetricUserCrashesWeeklyName[] = | 82 const char MetricsDaemon::kMetricUserCrashesWeeklyName[] = |
82 "Logging.UserCrashesWeekly"; | 83 "Logging.UserCrashesWeekly"; |
83 const char MetricsDaemon::kMetricCrashFrequencyMin = 1; | 84 const char MetricsDaemon::kMetricCrashFrequencyMin = 1; |
84 const char MetricsDaemon::kMetricCrashFrequencyMax = 100; | 85 const char MetricsDaemon::kMetricCrashFrequencyMax = 100; |
85 const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50; | 86 const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50; |
86 | 87 |
88 // disk stats metrics | |
89 | |
90 // The {Read,Write}Sectors numbers are in sectors/second. | |
91 // A sector is usually 512 bytes. | |
92 | |
93 const char MetricsDaemon::kMetricReadSectorsLongName[] = | |
94 "Filesystem.ReadSectorsLong"; | |
95 const char MetricsDaemon::kMetricWriteSectorsLongName[] = | |
96 "Filesystem.WriteSectorsLong"; | |
97 const char MetricsDaemon::kMetricReadSectorsShortName[] = | |
98 "Filesystem.ReadSectorsShort"; | |
99 const char MetricsDaemon::kMetricWriteSectorsShortName[] = | |
100 "Filesystem.WriteSectorsShort"; | |
101 | |
102 const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds | |
103 const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds | |
104 | |
105 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte | |
106 // sectors. | |
107 const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second | |
108 const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets | |
109 | |
110 // Path to disk stats. | |
111 const char MetricsDaemon::kMetricsDiskStatsPath[] = "/sys/class/block/sda/stat"; | |
petkov
2011/02/12 05:34:45
Unused? It seems that the current revision won't w
petkov
2011/02/13 17:28:47
Isn't this something like /sys/block/mmcblk0 on AR
Luigi Semenzato
2011/02/14 18:43:43
That path exists on my kaen, but the stats there s
| |
112 | |
87 // persistent metrics path | 113 // persistent metrics path |
88 const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; | 114 const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; |
89 | 115 |
90 | 116 |
91 // static | 117 // static |
92 const char* MetricsDaemon::kDBusMatches_[] = { | 118 const char* MetricsDaemon::kDBusMatches_[] = { |
93 "type='signal'," | 119 "type='signal'," |
94 "interface='" DBUS_IFACE_CRASH_REPORTER "'," | 120 "interface='" DBUS_IFACE_CRASH_REPORTER "'," |
95 "path='/'," | 121 "path='/'," |
96 "member='UserCrash'", | 122 "member='UserCrash'", |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
211 DeleteFrequencyCounters(); | 237 DeleteFrequencyCounters(); |
212 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); | 238 ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); |
213 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName); | 239 ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName); |
214 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName); | 240 ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName); |
215 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName); | 241 ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName); |
216 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName); | 242 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName); |
217 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); | 243 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); |
218 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); | 244 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); |
219 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); | 245 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); |
220 | 246 |
247 DiskStatsReporterInit(); | |
248 | |
221 // Don't setup D-Bus and GLib in test mode. | 249 // Don't setup D-Bus and GLib in test mode. |
222 if (testing) | 250 if (testing) |
223 return; | 251 return; |
224 | 252 |
225 g_thread_init(NULL); | 253 g_thread_init(NULL); |
226 g_type_init(); | 254 g_type_init(); |
227 dbus_g_thread_init(); | 255 dbus_g_thread_init(); |
228 | 256 |
229 DBusError error; | 257 DBusError error; |
230 dbus_error_init(&error); | 258 dbus_error_init(&error); |
(...skipping 12 matching lines...) Expand all Loading... | |
243 LOG_IF(FATAL, dbus_error_is_set(&error)) << | 271 LOG_IF(FATAL, dbus_error_is_set(&error)) << |
244 "unable to add a match: " << SAFE_MESSAGE(error); | 272 "unable to add a match: " << SAFE_MESSAGE(error); |
245 } | 273 } |
246 | 274 |
247 // Adds the D-Bus filter routine to be called back whenever one of | 275 // Adds the D-Bus filter routine to be called back whenever one of |
248 // the registered D-Bus matches is successful. The daemon is not | 276 // the registered D-Bus matches is successful. The daemon is not |
249 // activated for D-Bus messages that don't match. | 277 // activated for D-Bus messages that don't match. |
250 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); | 278 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); |
251 } | 279 } |
252 | 280 |
281 void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, | |
282 const char* diskstats_path) { | |
283 diskstats_path_ = diskstats_path; | |
284 Init(testing, metrics_lib); | |
285 } | |
286 | |
253 void MetricsDaemon::Loop() { | 287 void MetricsDaemon::Loop() { |
254 GMainLoop* loop = g_main_loop_new(NULL, false); | 288 GMainLoop* loop = g_main_loop_new(NULL, false); |
255 g_main_loop_run(loop); | 289 g_main_loop_run(loop); |
256 } | 290 } |
257 | 291 |
258 // static | 292 // static |
259 DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, | 293 DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, |
260 DBusMessage* message, | 294 DBusMessage* message, |
261 void* user_data) { | 295 void* user_data) { |
262 Time now = Time::Now(); | 296 Time now = Time::Now(); |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
487 // If there's a use monitor scheduled already, destroys it. | 521 // If there's a use monitor scheduled already, destroys it. |
488 if (usemon_source_ == NULL) | 522 if (usemon_source_ == NULL) |
489 return; | 523 return; |
490 | 524 |
491 DLOG(INFO) << "destroying use monitor"; | 525 DLOG(INFO) << "destroying use monitor"; |
492 g_source_destroy(usemon_source_); | 526 g_source_destroy(usemon_source_); |
493 usemon_source_ = NULL; | 527 usemon_source_ = NULL; |
494 usemon_interval_ = 0; | 528 usemon_interval_ = 0; |
495 } | 529 } |
496 | 530 |
531 void MetricsDaemon::DiskStatsReporterInit() { | |
532 DiskStatsReadStats(&read_sectors_, &write_sectors_); | |
533 // The first time around just run the long stat, so we don't delay boot. | |
534 diskstats_state_ = kDiskStatsLong; | |
535 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval); | |
536 } | |
537 | |
538 void MetricsDaemon::ScheduleDiskStatsCallback(int wait) { | |
539 if (testing_) { | |
540 return; | |
541 } | |
542 g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this); | |
543 } | |
544 | |
545 void MetricsDaemon::DiskStatsReadStats(long int* read_sectors, | |
546 long int* write_sectors) { | |
547 int nchars; | |
548 int nitems; | |
549 char line[200]; | |
550 int file = HANDLE_EINTR(open(diskstats_path_, O_RDONLY)); | |
551 if (file < 0) { | |
552 PLOG(WARNING) << "cannot open " << diskstats_path_; | |
553 return; | |
554 } | |
555 nchars = HANDLE_EINTR(read(file, line, sizeof(line))); | |
556 if (nchars < 0) { | |
557 PLOG(WARNING) << "cannot read from " << diskstats_path_; | |
558 goto close_file; | |
petkov
2011/02/12 05:34:45
goto is a bit evil. I'd suggestion two options:
-
Luigi Semenzato
2011/02/14 18:43:43
And a third option: an else clause. (It doesn't s
| |
559 } | |
560 LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in " | |
561 << diskstats_path_; | |
562 line[nchars] = '\0'; | |
563 nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld", | |
564 read_sectors, write_sectors); | |
565 LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in " | |
566 << diskstats_path_ << ", expected 2"; | |
567 close_file: | |
568 HANDLE_EINTR(close(file)); | |
569 } | |
570 | |
571 // static | |
572 gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) { | |
573 (static_cast<MetricsDaemon*>(handle))->DiskStatsCallback(); | |
574 return false; // one-time callback | |
575 } | |
576 | |
577 void MetricsDaemon::DiskStatsCallback() { | |
578 long int read_sectors_now, write_sectors_now; | |
579 DiskStatsReadStats(&read_sectors_now, &write_sectors_now); | |
580 | |
581 switch (diskstats_state_) { | |
582 | |
petkov
2011/02/12 05:34:45
remove blank line?
| |
583 case kDiskStatsShort: | |
584 SendMetric(kMetricReadSectorsShortName, | |
585 (int) (read_sectors_now - read_sectors_) / | |
586 kMetricDiskStatsShortInterval, | |
587 0, | |
petkov
2011/02/12 05:34:45
I think 0 is invalid minimum for some reason-- min
| |
588 kMetricSectorsIOMax, | |
589 kMetricSectorsBuckets); | |
590 SendMetric(kMetricWriteSectorsShortName, | |
591 (int) (write_sectors_now - write_sectors_) / | |
592 kMetricDiskStatsShortInterval, | |
593 0, | |
594 kMetricSectorsIOMax, | |
595 kMetricSectorsBuckets); | |
596 // Schedule long callback. | |
597 diskstats_state_ = kDiskStatsLong; | |
598 ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval - | |
599 kMetricDiskStatsShortInterval); | |
600 break; | |
601 | |
602 case kDiskStatsLong: | |
603 SendMetric(kMetricReadSectorsLongName, | |
604 (int) (read_sectors_now - read_sectors_) / | |
605 kMetricDiskStatsLongInterval, | |
606 0, | |
607 kMetricSectorsIOMax, | |
608 kMetricSectorsBuckets); | |
609 SendMetric(kMetricWriteSectorsLongName, | |
610 (int) (write_sectors_now - write_sectors_) / | |
611 kMetricDiskStatsLongInterval, | |
612 0, | |
613 kMetricSectorsIOMax, | |
614 kMetricSectorsBuckets); | |
615 // Reset sector counters | |
616 read_sectors_ = read_sectors_now; | |
617 write_sectors_ = write_sectors_now; | |
618 | |
619 // Schedule short callback. | |
620 diskstats_state_ = kDiskStatsShort; | |
621 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); | |
622 break; | |
623 | |
624 default: | |
625 LOG(FATAL) << "Invalid disk stats state"; | |
626 } | |
627 } | |
628 | |
497 // static | 629 // static |
498 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { | 630 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { |
499 if (count <= 0) | 631 if (count <= 0) |
500 return; | 632 return; |
501 | 633 |
502 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); | 634 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); |
503 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; | 635 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; |
504 daemon->SendMetric(kMetricDailyUseTimeName, minutes, | 636 daemon->SendMetric(kMetricDailyUseTimeName, minutes, |
505 kMetricDailyUseTimeMin, | 637 kMetricDailyUseTimeMin, |
506 kMetricDailyUseTimeMax, | 638 kMetricDailyUseTimeMax, |
507 kMetricDailyUseTimeBuckets); | 639 kMetricDailyUseTimeBuckets); |
508 } | 640 } |
509 | 641 |
510 void MetricsDaemon::SendMetric(const string& name, int sample, | 642 void MetricsDaemon::SendMetric(const string& name, int sample, |
511 int min, int max, int nbuckets) { | 643 int min, int max, int nbuckets) { |
512 DLOG(INFO) << "received metric: " << name << " " << sample << " " | 644 DLOG(INFO) << "received metric: " << name << " " << sample << " " |
513 << min << " " << max << " " << nbuckets; | 645 << min << " " << max << " " << nbuckets; |
514 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); | 646 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); |
515 } | 647 } |
OLD | NEW |