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> |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
95 const char MetricsDaemon::kMetricWriteSectorsLongName[] = | 95 const char MetricsDaemon::kMetricWriteSectorsLongName[] = |
96 "Platform.WriteSectorsLong"; | 96 "Platform.WriteSectorsLong"; |
97 const char MetricsDaemon::kMetricReadSectorsShortName[] = | 97 const char MetricsDaemon::kMetricReadSectorsShortName[] = |
98 "Platform.ReadSectorsShort"; | 98 "Platform.ReadSectorsShort"; |
99 const char MetricsDaemon::kMetricWriteSectorsShortName[] = | 99 const char MetricsDaemon::kMetricWriteSectorsShortName[] = |
100 "Platform.WriteSectorsShort"; | 100 "Platform.WriteSectorsShort"; |
101 | 101 |
102 const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds | 102 const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds |
103 const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds | 103 const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds |
104 | 104 |
105 const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds | |
106 | |
105 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte | 107 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte |
106 // sectors. | 108 // sectors. |
107 const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second | 109 const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second |
108 const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets | 110 const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets |
109 | 111 |
110 // persistent metrics path | 112 // persistent metrics path |
111 const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; | 113 const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; |
112 | 114 |
113 | 115 |
114 // static | 116 // static |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
241 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); | 243 ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); |
242 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); | 244 ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); |
243 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); | 245 ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); |
244 | 246 |
245 // Don't attempt to collect disk stats if there is no disk stats file. | 247 // Don't attempt to collect disk stats if there is no disk stats file. |
246 if (!diskstats_path.empty()) { | 248 if (!diskstats_path.empty()) { |
247 diskstats_path_ = diskstats_path; | 249 diskstats_path_ = diskstats_path; |
248 DiskStatsReporterInit(); | 250 DiskStatsReporterInit(); |
249 } | 251 } |
250 | 252 |
253 // Start collecting meminfo stats. | |
254 ScheduleMeminfoCallback(kMetricMeminfoInterval); | |
255 | |
251 // Don't setup D-Bus and GLib in test mode. | 256 // Don't setup D-Bus and GLib in test mode. |
252 if (testing) | 257 if (testing) |
253 return; | 258 return; |
254 | 259 |
255 g_thread_init(NULL); | 260 g_thread_init(NULL); |
256 g_type_init(); | 261 g_type_init(); |
257 dbus_g_thread_init(); | 262 dbus_g_thread_init(); |
258 | 263 |
259 DBusError error; | 264 DBusError error; |
260 dbus_error_init(&error); | 265 dbus_error_init(&error); |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
610 write_sectors_ = write_sectors_now; | 615 write_sectors_ = write_sectors_now; |
611 // Schedule short callback. | 616 // Schedule short callback. |
612 diskstats_state_ = kDiskStatsShort; | 617 diskstats_state_ = kDiskStatsShort; |
613 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); | 618 ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); |
614 break; | 619 break; |
615 default: | 620 default: |
616 LOG(FATAL) << "Invalid disk stats state"; | 621 LOG(FATAL) << "Invalid disk stats state"; |
617 } | 622 } |
618 } | 623 } |
619 | 624 |
625 void MetricsDaemon::ScheduleMeminfoCallback(int wait) { | |
626 if (testing_) { | |
627 return; | |
628 } | |
629 g_timeout_add_seconds(wait, MeminfoCallbackStatic, this); | |
630 } | |
631 | |
632 // static | |
633 gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { | |
634 return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback(); | |
635 } | |
636 | |
637 gboolean MetricsDaemon::MeminfoCallback() { | |
638 char meminfo[4096]; | |
639 int nchars; | |
640 const char* meminfo_path = "/proc/meminfo"; | |
641 int file = HANDLE_EINTR(open(meminfo_path, O_RDONLY)); | |
kmixter1
2011/04/08 23:04:05
Consider simplifying this by just using file_util:
Luigi Semenzato
2011/04/11 16:28:26
That's good, thanks. Output of /proc entries is l
| |
642 if (file < 0) { | |
643 PLOG(WARNING) << "cannot open " << meminfo_path; | |
644 return false; | |
645 } | |
646 nchars = HANDLE_EINTR(read(file, meminfo, sizeof(meminfo))); | |
647 HANDLE_EINTR(close(file)); | |
648 | |
649 if (nchars < 0) { | |
650 LOG(WARNING) << "cannot read from " << meminfo_path; | |
651 return false; | |
652 } | |
653 if (nchars == sizeof(meminfo)) { | |
654 LOG(WARNING) << meminfo_path << " was truncated"; | |
655 nchars -= 1; | |
656 } | |
657 meminfo[nchars] = '\0'; | |
658 return ProcessMeminfo(meminfo); | |
659 } | |
660 | |
661 gboolean MetricsDaemon::ProcessMeminfo(const char* meminfo) { | |
662 /* This array has one string for every item of /proc/meminfo that we want to | |
kmixter1
2011/04/08 23:04:05
Local style is to use // here and below.
Luigi Semenzato
2011/04/11 16:28:26
Cool, thanks.
| |
663 * report to UMA. They must be listed in the same order in which | |
664 * /proc/meminfo prints them. | |
665 */ | |
666 struct { | |
667 const char* name; /* print name */ | |
668 const char* match; /* string to match in output of proc/meminfo */ | |
669 int log_scale; /* report with log scale instead of linear percent */ | |
670 } fields[] = { | |
671 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory | |
kmixter1
2011/04/08 23:04:05
This change increases our current number of histog
Luigi Semenzato
2011/04/11 16:28:26
Yes---separate CL (different repo)
| |
672 { "MemFree", "MemFree" }, | |
673 { "Buffers", "Buffers" }, | |
674 { "Cached", "Cached" }, | |
675 // { "SwapCached", "SwapCached" }, | |
kmixter1
2011/04/08 23:04:05
Seems like if you're going to add spaces for align
Luigi Semenzato
2011/04/11 16:28:26
Off with the spaces!
| |
676 { "Active", "Active" }, | |
677 { "Inactive", "Inactive" }, | |
678 { "ActiveAnon", "Active(anon)" }, | |
679 { "InactiveAnon", "Inactive(anon)" }, | |
680 { "ActiveFile" , "Active(file)" }, | |
681 { "InactiveFile", "Inactive(file)" }, | |
682 { "Unevictable", "Unevictable", 1 }, | |
683 // { "Mlocked", "Mlocked" }, | |
684 // { "SwapTotal", "SwapTotal" }, | |
685 // { "SwapFree", "SwapFree" }, | |
686 // { "Dirty", "Dirty" }, | |
687 // { "Writeback", "Writeback" }, | |
688 { "AnonPages", "AnonPages" }, | |
689 { "Mapped", "Mapped" }, | |
690 { "Shmem", "Shmem", 1 }, | |
691 { "Slab", "Slab", 1 }, | |
kmixter1
2011/04/08 23:04:05
Suggest either getting rid of spaces for alignment
Luigi Semenzato
2011/04/11 16:28:26
Getting rid!
| |
692 // { "SReclaimable", "SReclaimable" }, | |
693 // { "SUnreclaim", "SUnreclaim" }, | |
694 }; | |
695 const int nfields = sizeof(fields) / sizeof(fields[0]); | |
kmixter1
2011/04/08 23:04:05
arraysize(fields)
Luigi Semenzato
2011/04/11 16:28:26
Yes!
| |
696 int total_memory = 0; | |
697 | |
698 /* Scan meminfo output and collect field values. Each field name has to | |
699 * match a meminfo entry (case insensitive) after removing non-alpha | |
700 * characters from the entry. */ | |
701 int i = 0; | |
702 const char* p = meminfo; | |
703 | |
704 for (;;) { | |
705 if (i == nfields) { | |
706 /* all fields are matched */ | |
707 return true; | |
708 } | |
709 if (*p == '\0') { | |
710 /* end of input reached while scanning */ | |
711 LOG(WARNING) << "cannot find field " << fields[i].match << | |
kmixter1
2011/04/08 23:04:05
with LOG statements, the style is to have wrapped
Luigi Semenzato
2011/04/11 16:28:26
Yes!
| |
712 " and following"; | |
713 return false; | |
714 } | |
715 if (strncmp(fields[i].match, p, strlen(fields[i].match)) == 0) { | |
716 /* name matches: parse value and report */ | |
717 int meminfo_value; | |
718 char metrics_name[128]; | |
719 char* rest; | |
720 p += strlen(fields[i].match); | |
kmixter1
2011/04/08 23:04:05
I would suggest finding a way to replace all this
Luigi Semenzato
2011/04/11 16:28:26
Yes!
On 2011/04/08 23:04:05, kmixter1 wrote:
| |
721 while (!isdigit(*p)) { | |
722 if (*p == '\0') { | |
723 LOG(WARNING) << "error parsing meminfo value"; | |
724 return false; | |
725 } | |
726 p++; | |
727 } | |
728 meminfo_value = (int) strtol(p, &rest, 10); | |
kmixter1
2011/04/08 23:04:05
static_cast
| |
729 if (p < rest) { | |
730 p = rest; | |
731 } else { | |
732 LOG(WARNING) << "missing meminfo value"; | |
733 return false; | |
734 } | |
735 if (i == 0) { | |
736 /* special case: total memory */ | |
737 total_memory = meminfo_value; | |
738 } else { | |
739 snprintf(metrics_name, sizeof(metrics_name), | |
740 "Platform.Meminfo%s", fields[i].name); | |
741 if (fields[i].log_scale) { | |
742 /* report value in kbytes, log scale, 4Gb max */ | |
743 SendMetric(metrics_name, meminfo_value, 1, 4 * 1000 * 1000, 100); | |
kmixter1
2011/04/08 23:04:05
This change increases our current number of histog
kmixter1
2011/04/08 23:04:05
So, doesn't this create a histogram with 4 million
Luigi Semenzato
2011/04/11 16:28:26
Thanks, yes, I have gone through that a couple of
Luigi Semenzato
2011/04/11 16:28:26
Yes that would be ironic.
But no, the number of b
kmixter1
2011/04/12 19:04:33
Apologies - I misread the code.
| |
744 } else { | |
745 /* report value as percent of total memory */ | |
746 int percent = meminfo_value * 100 / total_memory; | |
kmixter1
2011/04/08 23:04:05
this will crash if the kernel ever doesn't report
Luigi Semenzato
2011/04/11 16:28:26
Actually, it won't, because it won't get there if
| |
747 SendLinearMetric(metrics_name, percent, 100, 101); | |
748 } | |
749 } | |
750 /* start looking for next field */ | |
751 i++; | |
752 } | |
753 /* move to next input line */ | |
754 while (*p != '\n') { | |
755 if (*p == '\0') { | |
756 LOG(WARNING) << "error parsing meminfo line"; | |
757 return false; | |
758 } | |
759 p++; | |
760 } | |
761 p++; | |
762 } | |
763 } | |
764 | |
620 // static | 765 // static |
621 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { | 766 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { |
622 if (count <= 0) | 767 if (count <= 0) |
623 return; | 768 return; |
624 | 769 |
625 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); | 770 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); |
626 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; | 771 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; |
627 daemon->SendMetric(kMetricDailyUseTimeName, minutes, | 772 daemon->SendMetric(kMetricDailyUseTimeName, minutes, |
628 kMetricDailyUseTimeMin, | 773 kMetricDailyUseTimeMin, |
629 kMetricDailyUseTimeMax, | 774 kMetricDailyUseTimeMax, |
630 kMetricDailyUseTimeBuckets); | 775 kMetricDailyUseTimeBuckets); |
631 } | 776 } |
632 | 777 |
633 void MetricsDaemon::SendMetric(const string& name, int sample, | 778 void MetricsDaemon::SendMetric(const string& name, int sample, |
634 int min, int max, int nbuckets) { | 779 int min, int max, int nbuckets) { |
635 DLOG(INFO) << "received metric: " << name << " " << sample << " " | 780 DLOG(INFO) << "received metric: " << name << " " << sample << " " |
636 << min << " " << max << " " << nbuckets; | 781 << min << " " << max << " " << nbuckets; |
637 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); | 782 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); |
638 } | 783 } |
784 | |
785 void MetricsDaemon::SendLinearMetric(const string& name, int sample, | |
786 int max, int nbuckets) { | |
kmixter1
2011/04/08 23:04:05
I suggest not passing nbuckets since it's not used
Luigi Semenzato
2011/04/11 16:28:26
nbuckets is actually used. There is currently a c
| |
787 DLOG(INFO) << "received linear metric: " << name << " " << sample << " " | |
788 << max << " " << nbuckets; | |
789 /* TODO(semenzato): add a proper linear histogram to the Chrome external | |
790 * metrics API. */ | |
791 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale"; | |
792 metrics_lib_->SendEnumToUMA(name, sample, max); | |
kmixter1
2011/04/08 23:04:05
Ignoring the memory usage issue, I thought somewhe
Luigi Semenzato
2011/04/11 16:28:26
That's because "enum" is commonly used to send enu
| |
793 } | |
OLD | NEW |