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);