• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/metrics/psi_memory_parser.h"
6 
7 #include <stddef.h>
8 
9 #include <cinttypes>
10 #include <map>
11 #include <memory>
12 #include <string>
13 #include <string_view>
14 
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "components/metrics/metrics_log_store.h"
20 
21 namespace metrics {
22 
23 namespace {
24 
25 // Periods supported by standard Linux PSI metricvs.
26 constexpr uint32_t kMinCollectionInterval = 10;
27 constexpr uint32_t kMidCollectionInterval = 60;
28 constexpr uint32_t kMaxCollectionInterval = 300;
29 
30 constexpr uint32_t kDefaultCollectionInterval = kMinCollectionInterval;
31 
32 // Name of the histogram that represents the success and various failure modes
33 // for parsing PSI memory data.
34 const char kParsePSIMemoryHistogramName[] = "ChromeOS.CWP.ParsePSIMemory";
35 
36 constexpr std::string_view kContentPrefixSome = "some";
37 constexpr std::string_view kContentPrefixFull = "full";
38 constexpr std::string_view kContentTerminator = " total=";
39 constexpr std::string_view kMetricTerminator = " ";
40 
41 const char kMetricPrefixFormat[] = "avg%d=";
42 
43 }  // namespace
44 
PSIMemoryParser(uint32_t period)45 PSIMemoryParser::PSIMemoryParser(uint32_t period)
46     : period_(kDefaultCollectionInterval) {
47   if (period == kMinCollectionInterval || period == kMidCollectionInterval ||
48       period == kMaxCollectionInterval) {
49     period_ = period;
50   } else {
51     LOG(WARNING) << "Ignoring invalid interval [" << period << "]";
52   }
53 
54   metric_prefix_ = base::StringPrintf(kMetricPrefixFormat, period_);
55 }
56 
57 PSIMemoryParser::~PSIMemoryParser() = default;
58 
GetPeriod() const59 uint32_t PSIMemoryParser::GetPeriod() const {
60   return period_;
61 }
62 
GetMetricValue(std::string_view content,size_t start,size_t end)63 int PSIMemoryParser::GetMetricValue(std::string_view content,
64                                     size_t start,
65                                     size_t end) {
66   size_t value_start;
67   size_t value_end;
68   if (!internal::FindMiddleString(content, start, metric_prefix_,
69                                   kMetricTerminator, &value_start,
70                                   &value_end)) {
71     return -1;
72   }
73   if (value_end > end) {
74     return -1;  // Out of bounds of the search area.
75   }
76 
77   double n;
78   const std::string_view metric_value_text =
79       content.substr(value_start, value_end - value_start);
80   if (!base::StringToDouble(metric_value_text, &n)) {
81     return -1;  // Unable to convert string to number
82   }
83 
84   // Want to multiply by 100, but to avoid integer truncation,
85   // do best-effort rounding.
86   const int preround = static_cast<int>(n * 1000);
87   return (preround + 5) / 10;
88 }
89 
LogParseStatus(ParsePSIMemStatus stat)90 void PSIMemoryParser::LogParseStatus(ParsePSIMemStatus stat) {
91   constexpr int statCeiling =
92       static_cast<int>(ParsePSIMemStatus::kMaxValue) + 1;
93   base::UmaHistogramExactLinear(kParsePSIMemoryHistogramName,
94                                 static_cast<int>(stat), statCeiling);
95 }
96 
ParseMetrics(std::string_view content,int * metric_some,int * metric_full)97 ParsePSIMemStatus PSIMemoryParser::ParseMetrics(std::string_view content,
98                                                 int* metric_some,
99                                                 int* metric_full) {
100   size_t str_some_start;
101   size_t str_some_end;
102   size_t str_full_start;
103   size_t str_full_end;
104 
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
109 
110   DCHECK_NE(metric_some, nullptr);
111   DCHECK_NE(metric_full, nullptr);
112 
113   if (!internal::FindMiddleString(content, 0, kContentPrefixSome,
114                                   kContentTerminator, &str_some_start,
115                                   &str_some_end)) {
116     return ParsePSIMemStatus::kUnexpectedDataFormat;
117   }
118 
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;
124   }
125 
126   int compute_some = GetMetricValue(content, str_some_start, str_some_end);
127   if (compute_some < 0) {
128     return ParsePSIMemStatus::kInvalidMetricFormat;
129   }
130 
131   int compute_full = GetMetricValue(content, str_full_start, str_full_end);
132   if (compute_full < 0) {
133     return ParsePSIMemStatus::kInvalidMetricFormat;
134   }
135 
136   *metric_some = compute_some;
137   *metric_full = compute_full;
138 
139   return ParsePSIMemStatus::kSuccess;
140 }
141 
142 namespace internal {
143 
FindMiddleString(std::string_view content,size_t search_start,std::string_view prefix,std::string_view suffix,size_t * start,size_t * end)144 bool FindMiddleString(std::string_view content,
145                       size_t search_start,
146                       std::string_view prefix,
147                       std::string_view suffix,
148                       size_t* start,
149                       size_t* end) {
150   DCHECK_NE(start, nullptr);
151   DCHECK_NE(end, nullptr);
152 
153   size_t compute_start = content.find(prefix, search_start);
154   if (compute_start == std::string::npos) {
155     return false;
156   }
157   compute_start += prefix.length();
158 
159   size_t compute_end = content.find(suffix, compute_start);
160   if (compute_end == std::string::npos) {
161     return false;
162   }
163 
164   *start = compute_start;
165   *end = compute_end;
166 
167   return true;
168 }
169 
170 }  // namespace internal
171 
172 }  // namespace metrics
173