Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(704)

Side by Side Diff: metrics_daemon.cc

Issue 6804014: Add meminfo UMA collection. (Closed) Base URL: http://git.chromium.org/git/metrics.git@master
Patch Set: Add new fields and logarithmic histograms Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698