1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/metrics/psi_memory_parser.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/histogram_functions.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "components/metrics/metrics_log_store.h"
24 // Periods supported by standard Linux PSI metricvs.
25 constexpr uint32_t kMinCollectionInterval = 10;
26 constexpr uint32_t kMidCollectionInterval = 60;
27 constexpr uint32_t kMaxCollectionInterval = 300;
29 constexpr uint32_t kDefaultCollectionInterval = kMinCollectionInterval;
31 // Name of the histogram that represents the success and various failure modes
32 // for parsing PSI memory data.
33 const char kParsePSIMemoryHistogramName[] = "ChromeOS.CWP.ParsePSIMemory";
35 constexpr base::StringPiece kContentPrefixSome = "some";
36 constexpr base::StringPiece kContentPrefixFull = "full";
37 constexpr base::StringPiece kContentTerminator = " total=";
38 constexpr base::StringPiece kMetricTerminator = " ";
40 const char kMetricPrefixFormat[] = "avg%d=";
44 PSIMemoryParser::PSIMemoryParser(uint32_t period)
45 : period_(kDefaultCollectionInterval) {
46 if (period == kMinCollectionInterval || period == kMidCollectionInterval ||
47 period == kMaxCollectionInterval) {
50 LOG(WARNING) << "Ignoring invalid interval [" << period << "]";
53 metric_prefix_ = base::StringPrintf(kMetricPrefixFormat, period_);
56 PSIMemoryParser::~PSIMemoryParser() = default;
58 uint32_t PSIMemoryParser::GetPeriod() const {
62 int PSIMemoryParser::GetMetricValue(const base::StringPiece& content,
67 if (!internal::FindMiddleString(content, start, metric_prefix_,
68 kMetricTerminator, &value_start,
72 if (value_end > end) {
73 return -1; // Out of bounds of the search area.
77 const base::StringPiece metric_value_text =
78 content.substr(value_start, value_end - value_start);
79 if (!base::StringToDouble(metric_value_text, &n)) {
80 return -1; // Unable to convert string to number
83 // Want to multiply by 100, but to avoid integer truncation,
84 // do best-effort rounding.
85 const int preround = static_cast<int>(n * 1000);
86 return (preround + 5) / 10;
89 void PSIMemoryParser::LogParseStatus(ParsePSIMemStatus stat) {
90 constexpr int statCeiling =
91 static_cast<int>(ParsePSIMemStatus::kMaxValue) + 1;
92 base::UmaHistogramExactLinear(kParsePSIMemoryHistogramName,
93 static_cast<int>(stat), statCeiling);
96 ParsePSIMemStatus PSIMemoryParser::ParseMetrics(
97 const base::StringPiece& content,
100 size_t str_some_start;
102 size_t str_full_start;
105 // Example of content:
106 // some avg10=0.00 avg60=0.00 avg300=0.00 total=417963
107 // full avg10=0.00 avg60=0.00 avg300=0.00 total=205933
108 // we will pick one of the columns depending on the colleciton period set
110 DCHECK_NE(metric_some, nullptr);
111 DCHECK_NE(metric_full, nullptr);
113 if (!internal::FindMiddleString(content, 0, kContentPrefixSome,
114 kContentTerminator, &str_some_start,
116 return ParsePSIMemStatus::kUnexpectedDataFormat;
119 if (!internal::FindMiddleString(content,
120 str_some_end + kContentTerminator.length(),
121 kContentPrefixFull, kContentTerminator,
122 &str_full_start, &str_full_end)) {
123 return ParsePSIMemStatus::kUnexpectedDataFormat;
126 int compute_some = GetMetricValue(content, str_some_start, str_some_end);
127 if (compute_some < 0) {
128 return ParsePSIMemStatus::kInvalidMetricFormat;
131 int compute_full = GetMetricValue(content, str_full_start, str_full_end);
132 if (compute_full < 0) {
133 return ParsePSIMemStatus::kInvalidMetricFormat;
136 *metric_some = compute_some;
137 *metric_full = compute_full;
139 return ParsePSIMemStatus::kSuccess;
142 ParsePSIMemStatus PSIMemoryParser::ParseMetrics(const uint8_t* content,
146 // The cast below is admittedly sneaky, but inherently safe because
147 // we are translating a const pointer into another const pointer,
148 // and the data sizes of the pointed object are the same.
149 const char* string_content = reinterpret_cast<const char*>(content);
151 return ParseMetrics(base::StringPiece(string_content, len), metric_some,
157 bool FindMiddleString(const base::StringPiece& content,
159 const base::StringPiece& prefix,
160 const base::StringPiece& suffix,
163 DCHECK_NE(start, nullptr);
164 DCHECK_NE(end, nullptr);
166 size_t compute_start = content.find(prefix, search_start);
167 if (compute_start == std::string::npos) {
170 compute_start += prefix.length();
172 size_t compute_end = content.find(suffix, compute_start);
173 if (compute_end == std::string::npos) {
177 *start = compute_start;
183 } // namespace internal
185 } // namespace metrics