summaryrefslogtreecommitdiff
path: root/pinner/include/meminspect.h
blob: a2e09ec2f0652c3e380cdbc9a2d568b0e450017c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#pragma once

#include <android-base/stringprintf.h>
#include <fcntl.h>
#include <sys/endian.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <vector>
#include "ziparchive/zip_archive.h"

#define MEMINSPECT_FAIL_OPEN 1
#define MEMINSPECT_FAIL_FSTAT 2
#define MEMINSPECT_FAIL_MINCORE 3

#define DEFAULT_PAGES_PER_MINCORE 1

/**
 * This class stores an offset defined vma which exists
 * relative to another memory address.
 */
class VmaRange {
  public:
    uint32_t offset;
    uint32_t length;

    VmaRange() {}
    VmaRange(uint32_t off, uint32_t len) : offset(off), length(len) {}

    bool is_empty() const;

    /**
     * @brief Compute the intersection of this range with another range
     *
     * Intersection Operation:
     *
     * Example 1:
     * [   Range A    ]
     *          [   Range B   ]
     * Intersection:
     *          [  C  ]
     *
     * Example 2:
     * [   Range A    ]    [   Range B   ]
     * No Intersection
     *
     * @param target range to test against
     * @return the intersection range, if none is found, empty range is returned.
     */
    VmaRange intersect(const VmaRange& target) const;

    /**
     * @brief Merges the current range with a target range using a union operation
     * that is only successful when overlapping ranges occur.
     * A visual explanation can be seen as:
     *
     * Union-merge Operation:
     *
     * Example 1:
     * [   Range A    ]
     *          [   Range B   ]
     * Merged:
     * [       Range C        ]
     *
     * Example 2:
     * [   Range A    ]    [   Range B   ]
     * Fails, no merge available.
     *
     * @param target The range to test against.
     * @param result Upon successfully merging, contains the resulting range.
     * @return the merged range, if none is found, empty range is returned.
     */
    VmaRange union_merge(const VmaRange& target) const;

    uint32_t end_offset() const;
};

/**
 * Represents a set of memory ranges
 */
struct VmaRangeGroup {
    std::vector<VmaRange> ranges;

    /**
     * Compute intersection coverage between |range| and |this->ranges|
     * and append it to |out_memres|
     */
    void compute_coverage(const VmaRange& range, VmaRangeGroup& out_memres) const;

    /**
     * Apply an offset to all existing |ranges|.
     */
    void apply_offset(uint64_t offset);

    /**
     * Computes total resident bytes from existing set of memory ranges.
     */
    uint64_t compute_total_size();
};

/**
 * Represents useful immutable metadata for zip entry
 */
struct ZipEntryInfo {
    std::string name;
    uint64_t offset_in_zip;
    uint64_t file_size_bytes;
    uint64_t uncompressed_size;
};

/**
 * Represents the resident memory coverage for a zip entry within a zip file.
 */
struct ZipEntryCoverage {
    ZipEntryInfo info;

    /**
     * Contains all the coverage ranges if any have been computed with |compute_coverage|
     * and their offsets will be the absolute global offset from the zip file start.
     */
    VmaRangeGroup coverage;

    /**
     * Computes the intersection coverage for the current zip file entry
     * resident memory against a provided |probe| representing another set
     * of ranges.
     */
    ZipEntryCoverage compute_coverage(const VmaRangeGroup& probe) const;
};

// Class used for inspecting resident memory for entries within a zip file
class ZipMemInspector {
    /**
     * Stored probe of resident ranges either computed or provided by user.
     */
    VmaRangeGroup* probe_resident_ = nullptr;

    /**
     * List of file entries within zip file.
     */
    std::vector<ZipEntryInfo> entry_infos_;

    /**
     * Path to zip file.
     */
    std::string filename_;

    /**
     * Result of computing coverage operations.
     */
    std::vector<ZipEntryCoverage> entry_coverages_;

    /**
     * Handle that allows reading the zip entries.
     */
    ZipArchiveHandle handle_;

  public:
    ZipMemInspector(std::string filename) : filename_(filename) {}
    ~ZipMemInspector();

    /**
     * Reads zip file and computes resident memory coverage per zip entry if
     * a probe is provided, if no probe is provided, then whole file coverage
     * will be assumed.
     *
     * Note: If any zip entries have been manually added via |add_file_info|
     * then coverage will be only computed against manually added entries.
     *
     * @return 0 on success and 1 on error
     */
    int compute_per_file_coverage();

    /**
     * Computes resident memory for the entire zip file.
     *
     * @return 0 on success, 1 on failure
     */
    int probe_resident();

    /**
     * Retrieves the currently set probe if any exists.
     */
    VmaRangeGroup* get_probe();

    /**
     * Sets probe data in case you decide to pass a previously taken probe instead of a live taken
     * one.
     */
    void set_existing_probe(VmaRangeGroup* probe);

    /**
     * Returns the result of memory coverage of each file if any has been computed via
     * |compute_per_file_coverage|.
     */
    std::vector<ZipEntryCoverage>& get_file_coverages();

    /**
     * Returns the file information for each zip entry.
     */
    std::vector<ZipEntryInfo>& get_file_infos();

    /**
     * Add a zip entry manually.
     *
     * Note: Zip entries are usually retrieved by reading the |filename_| so
     * this method is mostly used for cases where client wants control of
     * zip file reading or for testing.
     */
    void add_file_info(ZipEntryInfo& file);

    /**
     * Computes the intersection coverage between provided |files| and |probe|.
     *
     * @return result of coverage computation
     */
    static std::vector<ZipEntryCoverage> compute_coverage(
            const std::vector<ZipEntryCoverage>& files, VmaRangeGroup* probe);

  private:
    /**
     * Read files and zip relative offsets for them.
     *
     * @return 0 on success, 1 on failure.
     */
    int read_files_and_offsets();
};

/**
 * Retrieve file size in bytes for |file|
 *
 * @return positive value with file size on success, otherwise, returns -1 on error.
 */
int64_t get_file_size(const std::string& file);

/**
 * @brief Probe resident memory for a currently opened file in the system.
 *
 * @param probed_file File to probe as defined by its path.
 * @param out_resident_mem Inspection result. This is populated when called.
 * @param pages_per_mincore Size of mincore window used, bigger means more memory used
 * during operation but slightly faster.
 * @return 0 on success or on failure a non-zero error code from the following list:
 * MEMINSPECT_FAIL_OPEN, MEMINSPECT_FAIL_FSTAT, MEMINSPECT_FAIL_MINCORE
 */
int probe_resident_memory(std::string probed_file, VmaRangeGroup& out_resident_mem,
                          int pages_per_mincore = DEFAULT_PAGES_PER_MINCORE);

/**
 * @brief Align vma ranges to a certain page size
 *
 * @param ranges vma ranges that have to be aligned
 * @param alignment Desired alignment, this is usually the page size.
 */
void align_ranges(std::vector<VmaRange>& ranges, unsigned int alignment);

/**
 * @brief Merges a list of ranges following a union-like merge which
 * means that two ranges that overlap will avoid double accounting for
 * overlaps.
 *
 * @param ranges vma ranges that need to be merged.
 * @return new vector with ranges merged.
 */
std::vector<VmaRange> merge_ranges(const std::vector<VmaRange>& ranges);