xref: /aosp_15_r20/system/extras/pinner/include/meminspect.h (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1 #pragma once
2 
3 #include <android-base/stringprintf.h>
4 #include <fcntl.h>
5 #include <sys/endian.h>
6 #include <sys/mman.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 #include <iostream>
11 #include <string>
12 #include <vector>
13 #include "ziparchive/zip_archive.h"
14 
15 #define MEMINSPECT_FAIL_OPEN 1
16 #define MEMINSPECT_FAIL_FSTAT 2
17 #define MEMINSPECT_FAIL_MINCORE 3
18 
19 #define DEFAULT_PAGES_PER_MINCORE 1
20 
21 /**
22  * This class stores an offset defined vma which exists
23  * relative to another memory address.
24  */
25 class VmaRange {
26   public:
27     uint32_t offset;
28     uint32_t length;
29 
VmaRange()30     VmaRange() {}
VmaRange(uint32_t off,uint32_t len)31     VmaRange(uint32_t off, uint32_t len) : offset(off), length(len) {}
32 
33     bool is_empty() const;
34 
35     /**
36      * @brief Compute the intersection of this range with another range
37      *
38      * Intersection Operation:
39      *
40      * Example 1:
41      * [   Range A    ]
42      *          [   Range B   ]
43      * Intersection:
44      *          [  C  ]
45      *
46      * Example 2:
47      * [   Range A    ]    [   Range B   ]
48      * No Intersection
49      *
50      * @param target range to test against
51      * @return the intersection range, if none is found, empty range is returned.
52      */
53     VmaRange intersect(const VmaRange& target) const;
54 
55     /**
56      * @brief Merges the current range with a target range using a union operation
57      * that is only successful when overlapping ranges occur.
58      * A visual explanation can be seen as:
59      *
60      * Union-merge Operation:
61      *
62      * Example 1:
63      * [   Range A    ]
64      *          [   Range B   ]
65      * Merged:
66      * [       Range C        ]
67      *
68      * Example 2:
69      * [   Range A    ]    [   Range B   ]
70      * Fails, no merge available.
71      *
72      * @param target The range to test against.
73      * @param result Upon successfully merging, contains the resulting range.
74      * @return the merged range, if none is found, empty range is returned.
75      */
76     VmaRange union_merge(const VmaRange& target) const;
77 
78     uint32_t end_offset() const;
79 };
80 
81 /**
82  * Represents a set of memory ranges
83  */
84 struct VmaRangeGroup {
85     std::vector<VmaRange> ranges;
86 
87     /**
88      * Compute intersection coverage between |range| and |this->ranges|
89      * and append it to |out_memres|
90      */
91     void compute_coverage(const VmaRange& range, VmaRangeGroup& out_memres) const;
92 
93     /**
94      * Apply an offset to all existing |ranges|.
95      */
96     void apply_offset(uint64_t offset);
97 
98     /**
99      * Computes total resident bytes from existing set of memory ranges.
100      */
101     uint64_t compute_total_size();
102 };
103 
104 /**
105  * Represents useful immutable metadata for zip entry
106  */
107 struct ZipEntryInfo {
108     std::string name;
109     uint64_t offset_in_zip;
110     uint64_t file_size_bytes;
111     uint64_t uncompressed_size;
112 };
113 
114 /**
115  * Represents the resident memory coverage for a zip entry within a zip file.
116  */
117 struct ZipEntryCoverage {
118     ZipEntryInfo info;
119 
120     /**
121      * Contains all the coverage ranges if any have been computed with |compute_coverage|
122      * and their offsets will be the absolute global offset from the zip file start.
123      */
124     VmaRangeGroup coverage;
125 
126     /**
127      * Computes the intersection coverage for the current zip file entry
128      * resident memory against a provided |probe| representing another set
129      * of ranges.
130      */
131     ZipEntryCoverage compute_coverage(const VmaRangeGroup& probe) const;
132 };
133 
134 // Class used for inspecting resident memory for entries within a zip file
135 class ZipMemInspector {
136     /**
137      * Stored probe of resident ranges either computed or provided by user.
138      */
139     VmaRangeGroup* probe_resident_ = nullptr;
140 
141     /**
142      * List of file entries within zip file.
143      */
144     std::vector<ZipEntryInfo> entry_infos_;
145 
146     /**
147      * Path to zip file.
148      */
149     std::string filename_;
150 
151     /**
152      * Result of computing coverage operations.
153      */
154     std::vector<ZipEntryCoverage> entry_coverages_;
155 
156     /**
157      * Handle that allows reading the zip entries.
158      */
159     ZipArchiveHandle handle_;
160 
161   public:
ZipMemInspector(std::string filename)162     ZipMemInspector(std::string filename) : filename_(filename) {}
163     ~ZipMemInspector();
164 
165     /**
166      * Reads zip file and computes resident memory coverage per zip entry if
167      * a probe is provided, if no probe is provided, then whole file coverage
168      * will be assumed.
169      *
170      * Note: If any zip entries have been manually added via |add_file_info|
171      * then coverage will be only computed against manually added entries.
172      *
173      * @return 0 on success and 1 on error
174      */
175     int compute_per_file_coverage();
176 
177     /**
178      * Computes resident memory for the entire zip file.
179      *
180      * @return 0 on success, 1 on failure
181      */
182     int probe_resident();
183 
184     /**
185      * Retrieves the currently set probe if any exists.
186      */
187     VmaRangeGroup* get_probe();
188 
189     /**
190      * Sets probe data in case you decide to pass a previously taken probe instead of a live taken
191      * one.
192      */
193     void set_existing_probe(VmaRangeGroup* probe);
194 
195     /**
196      * Returns the result of memory coverage of each file if any has been computed via
197      * |compute_per_file_coverage|.
198      */
199     std::vector<ZipEntryCoverage>& get_file_coverages();
200 
201     /**
202      * Returns the file information for each zip entry.
203      */
204     std::vector<ZipEntryInfo>& get_file_infos();
205 
206     /**
207      * Add a zip entry manually.
208      *
209      * Note: Zip entries are usually retrieved by reading the |filename_| so
210      * this method is mostly used for cases where client wants control of
211      * zip file reading or for testing.
212      */
213     void add_file_info(ZipEntryInfo& file);
214 
215     /**
216      * Computes the intersection coverage between provided |files| and |probe|.
217      *
218      * @return result of coverage computation
219      */
220     static std::vector<ZipEntryCoverage> compute_coverage(
221             const std::vector<ZipEntryCoverage>& files, VmaRangeGroup* probe);
222 
223   private:
224     /**
225      * Read files and zip relative offsets for them.
226      *
227      * @return 0 on success, 1 on failure.
228      */
229     int read_files_and_offsets();
230 };
231 
232 /**
233  * Retrieve file size in bytes for |file|
234  *
235  * @return positive value with file size on success, otherwise, returns -1 on error.
236  */
237 int64_t get_file_size(const std::string& file);
238 
239 /**
240  * @brief Probe resident memory for a currently opened file in the system.
241  *
242  * @param probed_file File to probe as defined by its path.
243  * @param out_resident_mem Inspection result. This is populated when called.
244  * @param pages_per_mincore Size of mincore window used, bigger means more memory used
245  * during operation but slightly faster.
246  * @return 0 on success or on failure a non-zero error code from the following list:
247  * MEMINSPECT_FAIL_OPEN, MEMINSPECT_FAIL_FSTAT, MEMINSPECT_FAIL_MINCORE
248  */
249 int probe_resident_memory(std::string probed_file, VmaRangeGroup& out_resident_mem,
250                           int pages_per_mincore = DEFAULT_PAGES_PER_MINCORE);
251 
252 /**
253  * @brief Align vma ranges to a certain page size
254  *
255  * @param ranges vma ranges that have to be aligned
256  * @param alignment Desired alignment, this is usually the page size.
257  */
258 void align_ranges(std::vector<VmaRange>& ranges, unsigned int alignment);
259 
260 /**
261  * @brief Merges a list of ranges following a union-like merge which
262  * means that two ranges that overlap will avoid double accounting for
263  * overlaps.
264  *
265  * @param ranges vma ranges that need to be merged.
266  * @return new vector with ranges merged.
267  */
268 std::vector<VmaRange> merge_ranges(const std::vector<VmaRange>& ranges);