Chromium Code Reviews| 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)); | |
| 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 | |
| 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; | |
| 668 const char* match; | |
| 669 } fields[] = { | |
| 670 { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory | |
| 671 { "MemFree", "MemFree" }, | |
| 672 { "Buffers", "Buffers" }, | |
| 673 { "Cached", "Cached" }, | |
| 674 // { "SwapCached", "SwapCached" }, | |
| 675 { "Active", "Active" }, | |
| 676 { "Inactive", "Inactive" }, | |
| 677 { "ActiveAnon", "Active(anon)" }, | |
| 678 { "InactiveAnon", "Inactive(anon)" }, | |
| 679 { "ActiveFile" , "Active(file)" }, | |
| 680 { "InactiveFile", "Inactive(file)" }, | |
| 681 // { "Unevictable", "Unevictable" }, | |
|
Mandeep Singh Baines
2011/04/06 22:35:26
Want this. Not percentage.
| |
| 682 // { "Mlocked", "Mlocked" }, | |
| 683 // { "SwapTotal", "SwapTotal" }, | |
| 684 // { "SwapFree", "SwapFree" }, | |
| 685 // { "Dirty", "Dirty" }, | |
| 686 // { "Writeback", "Writeback" }, | |
| 687 { "AnonPages", "AnonPages" }, | |
| 688 { "Mapped", "Mapped" }, | |
| 689 // { "Shmem", "Shmem" }, | |
|
Mandeep Singh Baines
2011/04/06 22:35:26
Want this. Not percentage (luigi's comment)
| |
| 690 // { "Slab", "Slab" }, | |
|
Mandeep Singh Baines
2011/04/06 22:35:26
Want this. Not percentage (luigi's comment)
| |
| 691 // { "SReclaimable", "SReclaimable" }, | |
| 692 // { "SUnreclaim", "SUnreclaim" }, | |
| 693 }; | |
| 694 const int nfields = sizeof(fields) / sizeof(fields[0]); | |
| 695 int total_memory = 0; | |
| 696 | |
| 697 /* Scan meminfo output and collect field values. Each field name has to | |
| 698 * match a meminfo entry (case insensitive) after removing non-alpha | |
| 699 * characters from the entry. */ | |
| 700 int i = 0; | |
| 701 const char* p = meminfo; | |
| 702 | |
| 703 for (;;) { | |
| 704 if (i == nfields) { | |
| 705 /* all fields are matched */ | |
| 706 return true; | |
| 707 } | |
| 708 if (*p == '\0') { | |
| 709 /* end of input reached while scanning */ | |
| 710 LOG(WARNING) << "cannot find field " << fields[i].match << | |
| 711 " and following"; | |
| 712 return false; | |
| 713 } | |
| 714 if (strncmp(fields[i].match, p, strlen(fields[i].match)) == 0) { | |
| 715 /* name matches: parse value and report */ | |
| 716 int meminfo_value; | |
| 717 char metrics_name[128]; | |
| 718 char* rest; | |
| 719 p += strlen(fields[i].match); | |
| 720 while (!isdigit(*p)) { | |
| 721 if (*p == '\0') { | |
| 722 LOG(WARNING) << "error parsing meminfo value"; | |
| 723 return false; | |
| 724 } | |
| 725 p++; | |
| 726 } | |
| 727 meminfo_value = (int) strtol(p, &rest, 10); | |
| 728 if (p < rest) { | |
| 729 p = rest; | |
| 730 } else { | |
| 731 LOG(WARNING) << "missing meminfo value"; | |
| 732 return false; | |
| 733 } | |
| 734 if (i == 0) { | |
| 735 /* special case: total memory */ | |
| 736 total_memory = meminfo_value; | |
| 737 } else { | |
| 738 /* report value as percent of total memory */ | |
| 739 snprintf(metrics_name, sizeof(metrics_name), | |
| 740 "Platform.Meminfo%s", fields[i].name); | |
| 741 SendLinearMetric(metrics_name, meminfo_value * 100 / total_memory, | |
| 742 100, 101); | |
| 743 } | |
| 744 /* start looking for next field */ | |
| 745 i++; | |
| 746 } | |
| 747 /* move to next input line */ | |
| 748 while (*p != '\n') { | |
| 749 if (*p == '\0') { | |
| 750 LOG(WARNING) << "error parsing meminfo line"; | |
| 751 return false; | |
| 752 } | |
| 753 p++; | |
| 754 } | |
| 755 p++; | |
| 756 } | |
| 757 } | |
| 758 | |
| 620 // static | 759 // static |
| 621 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { | 760 void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { |
| 622 if (count <= 0) | 761 if (count <= 0) |
| 623 return; | 762 return; |
| 624 | 763 |
| 625 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); | 764 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(handle); |
| 626 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; | 765 int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; |
| 627 daemon->SendMetric(kMetricDailyUseTimeName, minutes, | 766 daemon->SendMetric(kMetricDailyUseTimeName, minutes, |
| 628 kMetricDailyUseTimeMin, | 767 kMetricDailyUseTimeMin, |
| 629 kMetricDailyUseTimeMax, | 768 kMetricDailyUseTimeMax, |
| 630 kMetricDailyUseTimeBuckets); | 769 kMetricDailyUseTimeBuckets); |
| 631 } | 770 } |
| 632 | 771 |
| 633 void MetricsDaemon::SendMetric(const string& name, int sample, | 772 void MetricsDaemon::SendMetric(const string& name, int sample, |
| 634 int min, int max, int nbuckets) { | 773 int min, int max, int nbuckets) { |
| 635 DLOG(INFO) << "received metric: " << name << " " << sample << " " | 774 DLOG(INFO) << "received metric: " << name << " " << sample << " " |
| 636 << min << " " << max << " " << nbuckets; | 775 << min << " " << max << " " << nbuckets; |
| 637 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); | 776 metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); |
| 638 } | 777 } |
| 778 | |
| 779 void MetricsDaemon::SendLinearMetric(const string& name, int sample, | |
| 780 int max, int nbuckets) { | |
| 781 DLOG(INFO) << "received linear metric: " << name << " " << sample << " " | |
| 782 << max << " " << nbuckets; | |
| 783 /* TODO(semenzato): add a proper linear histogram to the Chrome external | |
| 784 * metrics API. */ | |
| 785 LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale"; | |
| 786 metrics_lib_->SendEnumToUMA(name, sample, max); | |
| 787 } | |
| OLD | NEW |