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 <fcntl.h> |
8 #include <string.h> | 8 #include <string.h> |
9 | 9 |
10 #include <base/file_util.h> | 10 #include <base/file_util.h> |
11 #include <base/logging.h> | 11 #include <base/logging.h> |
12 #include <base/string_util.h> | |
12 #include <dbus/dbus-glib-lowlevel.h> | 13 #include <dbus/dbus-glib-lowlevel.h> |
13 | 14 |
14 #include "counter.h" | 15 #include "counter.h" |
15 | 16 |
16 using base::Time; | 17 using base::Time; |
17 using base::TimeDelta; | 18 using base::TimeDelta; |
18 using base::TimeTicks; | 19 using base::TimeTicks; |
19 using std::string; | 20 using std::string; |
20 | 21 |
21 #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") | 22 #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
95 const char MetricsDaemon::kMetricWriteSectorsLongName[] = | 96 const char MetricsDaemon::kMetricWriteSectorsLongName[] = |
96 "Platform.WriteSectorsLong"; | 97 "Platform.WriteSectorsLong"; |
97 const char MetricsDaemon::kMetricReadSectorsShortName[] = | 98 const char MetricsDaemon::kMetricReadSectorsShortName[] = |
98 "Platform.ReadSectorsShort"; | 99 "Platform.ReadSectorsShort"; |
99 const char MetricsDaemon::kMetricWriteSectorsShortName[] = | 100 const char MetricsDaemon::kMetricWriteSectorsShortName[] = |
100 "Platform.WriteSectorsShort"; | 101 "Platform.WriteSectorsShort"; |
101 | 102 |
102 const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds | 103 const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds |
103 const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds | 104 const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds |
104 | 105 |
106 const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds | |
107 | |
105 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte | 108 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte |
106 // sectors. | 109 // sectors. |
107 const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second | 110 const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second |
108 const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets | 111 const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets |
109 | 112 |
110 // persistent metrics path | 113 // persistent metrics path |
111 const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; | 114 const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; |
112 | 115 |
113 | 116 |
114 // static | 117 // static |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
241 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); | 244 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); |
242 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); | 245 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); |
243 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); | 246 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); |
244 | 247 |
245 // Don't attempt to collect disk stats if there is no disk stats file. | 248 // Don't attempt to collect disk stats if there is no disk stats file. |
246 if (!diskstats_path.empty()) { | 249 if (!diskstats_path.empty()) { |
247 diskstats_path_ = diskstats_path; | 250 diskstats_path_ = diskstats_path; |
248 DiskStatsReporterInit(); | 251 DiskStatsReporterInit(); |
249 } | 252 } |
250 | 253 |
254 // Start collecting meminfo stats. | |
255 ScheduleMeminfoCallback(kMetricMeminfoInterval); | |
256 | |
251 // Don't setup D-Bus and GLib in test mode. | 257 // Don't setup D-Bus and GLib in test mode. |
252 if (testing) | 258 if (testing) |
253 return; | 259 return; |
254 | 260 |
255 g_thread_init(NULL); | 261 g_thread_init(NULL); |
256 g_type_init(); | 262 g_type_init(); |
257 dbus_g_thread_init(); | 263 dbus_g_thread_init(); |
258 | 264 |
259 DBusError error; | 265 DBusError error; |
260 dbus_error_init(&error); | 266 dbus_error_init(&error); |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
610 write_sectors_ = write_sectors_now; | 616 write_sectors_ = write_sectors_now; |
611 // Schedule short callback. | 617 // Schedule short callback. |
612 diskstats_state_ = kDiskStatsShort; | 618 diskstats_state_ = kDiskStatsShort; |
613 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); | 619 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); |
614 break; | 620 break; |
615 default: | 621 default: |
616 LOG(FATAL) << "Invalid disk stats state"; | 622 LOG(FATAL) << "Invalid disk stats state"; |
617 } | 623 } |
618 } | 624 } |
619 | 625 |
626 void MetricsDaemon::ScheduleMeminfoCallback(int wait) { | |
627 if (testing_) { | |
628 return; | |
629 } | |
630 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this); | |
631 } | |
632 | |
633 // static | |
634 gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { | |
635 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback(); | |
636 } | |
637 | |
638 gboolean MetricsDaemon::MeminfoCallback() { | |
639 std::string meminfo; | |
640 const FilePath meminfo_path("/proc/meminfo"); | |
641 if (!file_util::ReadFileToString(meminfo_path, &meminfo)) { | |
642 LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); | |
643 return false; | |
644 } | |
645 return ProcessMeminfo(meminfo); | |
646 } | |
647 | |
648 gboolean MetricsDaemon::ProcessMeminfo(std::string meminfo) { | |
649 // This array has one element for every item of /proc/meminfo that we want to | |
650 // report to UMA. They must be listed in the same order in which | |
651 // /proc/meminfo prints them. | |
652 struct { | |
653 const char* name; // print name | |
654 const char* match; // string to match in output of /proc/meminfo | |
655 int log_scale; // report with log scale instead of linear percent | |
656 } fields[] = { | |
657 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory | |
658 { "MemFree", "MemFree" }, | |
659 { "Buffers", "Buffers" }, | |
660 { "Cached", "Cached" }, | |
661 // { "SwapCached", "SwapCached" }, | |
662 { "Active", "Active" }, | |
663 { "Inactive", "Inactive" }, | |
664 { "ActiveAnon", "Active(anon)" }, | |
665 { "InactiveAnon", "Inactive(anon)" }, | |
666 { "ActiveFile" , "Active(file)" }, | |
667 { "InactiveFile", "Inactive(file)" }, | |
668 { "Unevictable", "Unevictable", 1 }, | |
669 // { "Mlocked", "Mlocked" }, | |
670 // { "SwapTotal", "SwapTotal" }, | |
671 // { "SwapFree", "SwapFree" }, | |
672 // { "Dirty", "Dirty" }, | |
673 // { "Writeback", "Writeback" }, | |
674 { "AnonPages", "AnonPages" }, | |
675 { "Mapped", "Mapped" }, | |
676 { "Shmem", "Shmem", 1 }, | |
677 { "Slab", "Slab", 1 }, | |
678 // { "SReclaimable", "SReclaimable" }, | |
679 // { "SUnreclaim", "SUnreclaim" }, | |
680 }; | |
681 const int nfields = sizeof(fields) / sizeof(fields[0]); // arraysize no-no | |
kmixter1
2011/04/12 19:04:33
why is it a no-no?
Luigi Semenzato
2011/04/12 21:12:32
I don't know. Maybe it doesn't like anonymous str
| |
682 int total_memory = 0; | |
683 std::vector<std::string> lines; | |
684 int nlines = Tokenize(meminfo, "\n", &lines); | |
685 | |
686 // Scan meminfo output and collect field values. Each field name has to | |
687 // match a meminfo entry (case insensitive) after removing non-alpha | |
688 // characters from the entry. | |
689 int i = 0; | |
690 int iline = 0; | |
691 for (;;) { | |
692 if (i == nfields) { | |
693 // all fields are matched | |
694 return true; | |
695 } | |
696 if (iline == nlines) { | |
697 // end of input reached while scanning | |
698 LOG(WARNING) << "cannot find field " << fields[i].match | |
699 << " and following"; | |
700 return false; | |
701 } | |
702 | |
703 std::vector<std::string> tokens; | |
704 Tokenize(lines[iline], ": ", &tokens); | |
705 | |
706 if (strcmp(fields[i].match, tokens[0].c_str()) == 0) { | |
707 // name matches: parse value and report | |
708 int meminfo_value; | |
709 char metrics_name[128]; | |
710 char* rest; | |
711 meminfo_value = static_cast<int>(strtol(tokens[1].c_str(), &rest, 10)); | |
712 if (*rest != '\0') { | |
713 LOG(WARNING) << "missing meminfo value"; | |
714 return false; | |
715 } | |
716 if (i == 0) { | |
717 // special case: total memory | |
718 total_memory = meminfo_value; | |
719 } else { | |
720 snprintf(metrics_name, sizeof(metrics_name), | |
721 "Platform.Meminfo%s", fields[i].name); | |
722 if (fields[i].log_scale) { | |
723 // report value in kbytes, log scale, 4Gb max | |
724 SendMetric(metrics_name, meminfo_value, 1, 4 * 1000 * 1000, 100); | |
725 } else { | |
726 // report value as percent of total memory | |
727 if (total_memory == 0) { | |
728 // this "cannot happen" | |
729 LOG(WARNING) << "borked meminfo parser"; | |
730 return false; | |
731 } | |
732 int percent = meminfo_value * 100 / total_memory; | |
kmixter1
2011/04/12 19:04:33
total_memory could have been reported as 0 and rea
Luigi Semenzato
2011/04/12 21:12:32
Yes. So now we can't reach this statement when to
| |
733 SendLinearMetric(metrics_name, percent, 100, 101); | |
734 } | |
735 } | |
736 // start looking for next field | |
737 i++; | |
738 } | |
739 iline++; | |
740 } | |
741 } | |
742 | |
620 // static | 743 // static |
621 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { | 744 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { |
622 if (count <= 0) | 745 if (count <= 0) |
623 return; | 746 return; |
624 | 747 |
625 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); | 748 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); |
626 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; | 749 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; |
627 daemon->SendMetric(kMetricDailyUseTimeName, minutes, | 750 daemon->SendMetric(kMetricDailyUseTimeName, minutes, |
628 kMetricDailyUseTimeMin, | 751 kMetricDailyUseTimeMin, |
629 kMetricDailyUseTimeMax, | 752 kMetricDailyUseTimeMax, |
630 kMetricDailyUseTimeBuckets); | 753 kMetricDailyUseTimeBuckets); |
631 } | 754 } |
632 | 755 |
633 void MetricsDaemon::SendMetric(const string& name, int sample, | 756 void MetricsDaemon::SendMetric(const string& name, int sample, |
634 int min, int max, int nbuckets) { | 757 int min, int max, int nbuckets) { |
635 DLOG(INFO) << "received metric: " << name << " " << sample << " " | 758 DLOG(INFO) << "received metric: " << name << " " << sample << " " |
636 << min << " " << max << " " << nbuckets; | 759 << min << " " << max << " " << nbuckets; |
637 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); | 760 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); |
638 } | 761 } |
762 | |
763 void MetricsDaemon::SendLinearMetric(const string& name, int sample, | |
764 int max, int nbuckets) { | |
765 DLOG(INFO) << "received linear metric: " << name << " " << sample << " " | |
766 << max << " " << nbuckets; | |
767 /* TODO(semenzato): add a proper linear histogram to the Chrome external | |
kmixter1
2011/04/12 19:04:33
Prefer // here.
Luigi Semenzato
2011/04/12 21:12:32
Thanks, missed that one.
| |
768 * metrics API. */ | |
769 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale"; | |
kmixter1
2011/04/12 19:04:33
Ah, I understand. Fair enough - you're forcing th
| |
770 metrics_lib_->SendEnumToUMA(name, sample, max); | |
771 } | |
OLD | NEW |