1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef HARDWARE_GOOGLE_PIXEL_PIXELSTATS_MMMETRICSREPORTER_H
18 #define HARDWARE_GOOGLE_PIXEL_PIXELSTATS_MMMETRICSREPORTER_H
19 
20 #include <map>
21 #include <string>
22 
23 #include <aidl/android/frameworks/stats/IStats.h>
24 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
25 
26 namespace android {
27 namespace hardware {
28 namespace google {
29 namespace pixel {
30 
31 using aidl::android::frameworks::stats::IStats;
32 using aidl::android::frameworks::stats::VendorAtomValue;
33 
34 /**
35  * A class to upload Pixel MM health metrics
36  */
37 class MmMetricsReporter {
38   public:
39     // Define the enum based on the group range names
40     enum OomScoreAdjGroup : int32_t {
41         OOMR_950 = 0,
42         OOMR_900,
43         OOMR_850,
44         OOMR_800,
45         OOMR_750,
46         OOMR_700,
47         OOMR_650,
48         OOMR_600,
49         OOMR_550,
50         OOMR_500,
51         OOMR_450,
52         OOMR_400,
53         OOMR_350,
54         OOMR_300,
55         OOMR_250,
56         OOMR_200,
57         OOMS_200,
58         OOMR_150,
59         OOMR_100,
60         OOMR_050,
61         OOMR_000,
62         OOMS_000,
63         OOMR_NEGATIVE,
64         OOM_NUM_OF_GROUPS,
65     };
66 
67     struct OomGroupMemUsage {
68         OomScoreAdjGroup oom_group;  // the diemsion field
69         int64_t nr_task;
70         int64_t file_rss_kb;
71         int64_t anon_rss_kb;
72         int64_t pgtable_kb;
73         int64_t swap_ents_kb;
74         int64_t shmem_rss_kb;
75     };
76 
77     MmMetricsReporter();
78     void aggregatePixelMmMetricsPer5Min();
79     void logPixelMmMetricsPerHour(const std::shared_ptr<IStats> &stats_client);
80     void logPixelMmMetricsPerDay(const std::shared_ptr<IStats> &stats_client);
81     void logGcmaPerDay(const std::shared_ptr<IStats> &stats_client);
82     void logGcmaPerHour(const std::shared_ptr<IStats> &stats_client);
83     void logMmProcessUsageByOomGroupSnapshot(const std::shared_ptr<IStats> &stats_client);
84     void logCmaStatus(const std::shared_ptr<IStats> &stats_client);
85     std::vector<VendorAtomValue> genPixelMmMetricsPerHour();
86     std::vector<VendorAtomValue> genPixelMmMetricsPerDay();
87     bool readMmProcessUsageByOomGroup(std::vector<OomGroupMemUsage> *ogusage);
88     std::vector<VendorAtomValue> genMmProcessUsageByOomGroupSnapshotAtom(
89             const OomGroupMemUsage &data);
90     std::vector<VendorAtomValue> readAndGenGcmaPerHour();
91     std::vector<VendorAtomValue> readAndGenGcmaPerDay();
~MmMetricsReporter()92     virtual ~MmMetricsReporter() {}
93 
94   private:
95     struct MmMetricsInfo {
96         std::string name;
97         int atom_key;
98         bool update_diff;
99     };
100 
101     /*
102      * Similar to MmMetricsInfo, but /proc/stat output is an array rather
103      * than one single value.  So we need an offset to get the specific value
104      * in the array.
105      * special: offset = -1 means to get the sum of the elements in the array.
106      */
107     struct ProcStatMetricsInfo {
108         std::string name;
109         int offset;
110         int atom_key;
111         bool update_diff;
112     };
113 
114     enum CmaType {
115         FARAWIMG = 0,
116         FAIMG = 1,
117         FATPU = 2,
118         FAPREV = 3,
119         VFRAME = 4,
120         VSTREAM = 5,
121     };
122 
123     static const std::vector<MmMetricsInfo> kMmMetricsPerHourInfo;
124     static const std::vector<MmMetricsInfo> kMeminfoInfo;
125     static const std::vector<MmMetricsInfo> kMmMetricsPerDayInfo;
126     static const std::vector<ProcStatMetricsInfo> kProcStatInfo;
127     static const std::vector<MmMetricsInfo> kCmaStatusInfo;
128     static const std::vector<MmMetricsInfo> kCmaStatusExtInfo;
129 
130     // raw PSI
131     static constexpr const char *kPsiBasePath = "/proc/pressure";
132     static constexpr const char *kPsiTypes[3] = {"cpu", "io", "memory"};
133     static constexpr const char *kPsiCategories[2] = {"full", "some"};
134     static constexpr const char *kPsiMetricNames[4] = {"avg10", "avg60", "avg300", "total"};
135     static constexpr int kPsiNumFiles = sizeof(kPsiTypes) / sizeof(kPsiTypes[0]);
136     static constexpr int kPsiNumCategories = sizeof(kPsiCategories) / sizeof(kPsiCategories[0]);
137     // number of statistics metric names (one total and several timed averages, per category)
138     static constexpr int kPsiNumNames = sizeof(kPsiMetricNames) / sizeof(kPsiMetricNames[0]);
139 
140     // Though cpu has no 'full' category, here we assume it has
141     // So, all file will contain 2 lines x 4 metrics per line = 8 metrics total.
142     static constexpr int kPsiMetricsPerFile = kPsiNumCategories * kPsiNumNames;
143 
144     // we have 1 'total' and all others 'averages' per category
145     // "total" metrics are already accumulative and thus no aggregation is needed.
146     //  raw values are used.
147     static constexpr int kPsiNumTotals = 1;
148     static constexpr int kPsiNumAvgs = kPsiNumNames - kPsiNumTotals;
149 
150     // -1 since "cpu" type has no "full" category
151     static constexpr int kPsiNumAllCategories = kPsiNumFiles * kPsiNumCategories - 1;
152 
153     // number of raw metrics: total and avgs, and the combined all: added together.
154     static constexpr int kPsiNumAllTotals = kPsiNumAllCategories * kPsiNumTotals;
155     static constexpr int kPsiNumAllAvgs = kPsiNumAllCategories * kPsiNumAvgs;
156     static constexpr int kPsiNumAllMetrics = kPsiNumAllTotals + kPsiNumAllAvgs;
157 
158     // aggregated into (1) min, (2) max, (3) average (internally the sum is kept than the average)
159     static constexpr int kPsiNumOfAggregatedType = 3;
160 
161     // # of upload metrics will have a aggregation factor on all 'average' type raw metrics.
162     static constexpr int kPsiNumAllUploadAvgMetrics = kPsiNumAllAvgs * kPsiNumOfAggregatedType;
163     static constexpr int kPsiNumAllUploadTotalMetrics = kPsiNumAllTotals;
164     static constexpr int kPsiNumAllUploadMetrics =
165             kPsiNumAllUploadTotalMetrics + kPsiNumAllUploadAvgMetrics;
166 
167     bool checkKernelMMMetricSupport();
168     bool checkKernelOomUsageSupport();
169     bool checkKernelGcmaSupport();
170 
MmMetricsSupported()171     bool MmMetricsSupported() { return ker_mm_metrics_support_; }
OomUsageSupoorted()172     bool OomUsageSupoorted() { return ker_oom_usage_support_; }
GcmaSupported()173     bool GcmaSupported() { return ker_gcma_support_; }
174 
175     bool ReadFileToUint(const std::string &path, uint64_t *val);
176     bool reportVendorAtom(const std::shared_ptr<IStats> &stats_client, int atom_id,
177                           const std::vector<VendorAtomValue> &values, const std::string &atom_name);
178     void readCompactionDurationStat(std::vector<long> *store);
179     void fillCompactionDurationStatAtom(const std::vector<long> &store,
180                                         std::vector<VendorAtomValue> *values);
181     void readDirectReclaimStat(std::vector<long> *store);
182     void fillDirectReclaimStatAtom(const std::vector<long> &store,
183                                    std::vector<VendorAtomValue> *values);
184     void readPressureStall(const std::string &basePath, std::vector<long> *store);
185     bool parsePressureStallFileContent(bool is_cpu, const std::string &lines,
186                                        std::vector<long> *store, int file_save_idx);
187     bool parsePressureStallWords(const std::vector<std::string> &words, std::vector<long> *store,
188                                  int line_save_idx);
189     bool savePressureMetrics(const std::string &name, const std::string &value,
190                              std::vector<long> *store, int base_save_idx);
191     void fillPressureStallAtom(std::vector<VendorAtomValue> *values);
192     void aggregatePressureStall();
193     std::map<std::string, uint64_t> readSysfsNameValue(const std::string &path);
194     std::map<std::string, std::vector<uint64_t>> readProcStat(const std::string &path);
195     uint64_t getIonTotalPools();
196     uint64_t getGpuMemory();
197     bool fillAtomValues(const std::vector<MmMetricsInfo> &metrics_info,
198                         const std::map<std::string, uint64_t> &mm_metrics,
199                         std::map<std::string, uint64_t> *prev_mm_metrics,
200                         std::vector<VendorAtomValue> *atom_values);
201     bool getValueFromParsedProcStat(const std::map<std::string, std::vector<uint64_t>> pstat,
202                                     const std::string &name, int offset, uint64_t *output);
203     bool fillProcStat(const std::vector<ProcStatMetricsInfo> &metrics_info,
204                       const std::map<std::string, std::vector<uint64_t>> &cur_pstat,
205                       std::map<std::string, std::vector<uint64_t>> *prev_pstat,
206                       std::vector<VendorAtomValue> *atom_values);
207     virtual std::string getProcessStatPath(const std::string &name, int *prev_pid);
208     bool isValidProcessInfoPath(const std::string &path, const char *name);
209     int findPidByProcessName(const std::string &name);
210     int64_t getStimeByPathAndVerifyName(const std::string &path, const std::string &name);
211     void fillProcessStime(int atom_key, const std::string &name, int *pid, uint64_t *prev_stime,
212                           std::vector<VendorAtomValue> *atom_values);
213     std::map<std::string, uint64_t> readCmaStat(const std::string &cma_type,
214                                                 const std::vector<MmMetricsInfo> &metrics_info);
215     void reportCmaStatusAtom(
216             const std::shared_ptr<IStats> &stats_client, int atom_id, const std::string &cma_type,
217             int cma_name_offset, const std::vector<MmMetricsInfo> &metrics_info,
218             std::map<std::string, std::map<std::string, uint64_t>> *all_prev_cma_stat);
219 
220     std::optional<OomGroupMemUsage> parseMmProcessUsageByOomGroupLine(const std::string &line);
221     bool readMmProcessUsageByOomGroupFile(const std::string &path,
222                                           std::vector<OomGroupMemUsage> *ogusage, int32_t *m_uid);
223 
224     // test code could override this to inject test data
225     // though named 'Sysfs', it can be applied to proc fs
getSysfsPath(const std::string & path)226     virtual std::string getSysfsPath(const std::string &path) { return path; }
227 
228     const char *const kVmstatPath;
229     const char *const kIonTotalPoolsPath;
230     const char *const kIonTotalPoolsPathForLegacy;
231     const char *const kGpuTotalPages;
232     const char *const kCompactDuration;
233     const char *const kDirectReclaimBasePath;
234     const char *const kPixelStatMm;
235     const char *const kMeminfoPath;
236     const char *const kProcStatPath;
237     const char *const kProcVendorMmUsageByOom;
238     const char *const kGcmaBasePath;
239 
240     // GCMA hourly metrics
241     const char *const kGcmaCached = "cached";
242 
243     // GCMA hourly 1/2
244     const char *const kGcmaHourlySimpleKnobs[4] = {
245             "discarded",
246             "evicted",
247             "loaded",
248             "stored",
249     };
250 
251     // GCMA hourly 2/2
252     const char *const kGcmaHourlyHistogramKnobs[4] = {
253             "latency_low",
254             "latency_mid",
255             "latency_high",
256             "latency_extreme_high",
257     };
258 
259     // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
260     // store everything in the values array at the index of the field number
261     // -2.
262     static constexpr int kVendorAtomOffset = 2;
263     static constexpr int kNumCompactionDurationPrevMetrics = 6;
264     static constexpr int kNumDirectReclaimPrevMetrics = 20;
265 
266     std::vector<long> prev_compaction_duration_;
267     std::vector<long> prev_direct_reclaim_;
268     long prev_psi_total_[kPsiNumAllTotals];
269     long psi_total_[kPsiNumAllTotals];
270     long psi_aggregated_[kPsiNumAllUploadAvgMetrics];  // min, max and avg of original avgXXX
271     int psi_data_set_count_ = 0;
272     std::map<std::string, uint64_t> prev_hour_vmstat_;
273     std::map<std::string, uint64_t> prev_day_vmstat_;
274     std::map<std::string, uint64_t> prev_day_pixel_vmstat_;
275     std::map<std::string, std::vector<uint64_t>> prev_procstat_;
276     std::map<std::string, std::map<std::string, uint64_t>> prev_cma_stat_;
277     std::map<std::string, std::map<std::string, uint64_t>> prev_cma_stat_ext_;
278     int prev_kswapd_pid_ = -1;
279     int prev_kcompactd_pid_ = -1;
280     uint64_t prev_kswapd_stime_ = 0;
281     uint64_t prev_kcompactd_stime_ = 0;
282     int32_t oom_usage_uid_ = 0;
283     bool ker_mm_metrics_support_;
284     bool ker_oom_usage_support_;
285     bool ker_gcma_support_;
286 };
287 
288 }  // namespace pixel
289 }  // namespace google
290 }  // namespace hardware
291 }  // namespace android
292 
293 #endif  // HARDWARE_GOOGLE_PIXEL_PIXELSTATS_MMMETRICSREPORTER_H
294