1 #include <android-base/parseint.h>
2 #include <fcntl.h>
3 #include <sys/endian.h>
4 #include <sys/mman.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <iostream>
9 #include <string>
10 #include <vector>
11
12 #include <meminspect.h>
13 #include <pin_utils.h>
14
15 using namespace std;
16 using namespace android::base;
17
18 enum ToolMode {
19 MAPPED_FILE, // Files that are mapped in memory
20 PINLIST, // pinlist.meta style file
21 UNKNOWN
22 };
23
print_pinlist_ranges(const std::vector<VmaRange> & ranges)24 void print_pinlist_ranges(const std::vector<VmaRange>& ranges) {
25 cout << "--pinlist memory ranges--" << endl;
26 for (auto&& range : ranges) {
27 cout << "start=" << range.offset << " bytes=" << range.length << endl;
28 }
29 }
30
print_pinlist_summary(const std::vector<VmaRange> & ranges)31 void print_pinlist_summary(const std::vector<VmaRange>& ranges) {
32 cout << "--pinlist summary--" << endl;
33 uint64_t total_bytes = 0;
34 for (auto&& range : ranges) {
35 total_bytes += range.length;
36 }
37 cout << "total_bytes_to_pin=" << total_bytes << endl;
38 }
39
perform_file_action(const vector<string> & options)40 int perform_file_action(const vector<string>& options) {
41 std::string custom_probe_file;
42 std::string output_file;
43 std::string pinconfig_file;
44
45 bool verbose = false;
46 bool is_zip = false;
47 bool dump_results = false;
48 ProbeType probe_type = UNSET;
49 int64_t write_quota = -1; // unbounded by default
50
51 if (options.empty()) {
52 cerr << "Missing filename for file action, see usage for details." << endl;
53 return 1;
54 }
55
56 std::string input_file = options[0];
57
58 // Validate that the file exists.
59 if (get_file_size(input_file) == -1) {
60 cerr << "Error: Could not read file: " << input_file << endl;
61 return 1;
62 }
63
64 if (input_file.empty()) {
65 cerr << "Error: Should specify an input file." << endl;
66 return 1;
67 }
68
69 // Parse flags
70 for (int i = 1; i < options.size(); ++i) {
71 string option = options[i];
72 if (option == "--gen-probe") {
73 if (probe_type != ProbeType::UNSET) {
74 cerr << "Should only specify one probe treatment. See usage for details." << endl;
75 return 1;
76 }
77 probe_type = ProbeType::GENERATE;
78 continue;
79 }
80
81 if (option == "--use-probe") {
82 if (probe_type != ProbeType::UNSET) {
83 cerr << "Should only specify one probe treatment. See usage for details." << endl;
84 return 1;
85 }
86 probe_type = ProbeType::CUSTOM;
87 ++i;
88 custom_probe_file = options[i];
89 continue;
90 }
91 if (option == "--pinconfig") {
92 ++i;
93 pinconfig_file = options[i];
94 continue;
95 }
96 if (option == "-o") {
97 ++i;
98 output_file = options[i];
99 continue;
100 }
101 if (option == "--quota") {
102 ++i;
103 android::base::ParseInt(options[i], &write_quota);
104 continue;
105 }
106 if (option == "-v") {
107 verbose = true;
108 continue;
109 }
110 if (option == "--zip") {
111 is_zip = true;
112 continue;
113 }
114 if (option == "--dump") {
115 dump_results = true;
116 continue;
117 }
118 }
119
120 if (verbose) {
121 cout << "Setting output pinlist file: " << output_file.c_str() << endl;
122 cout << "Setting input file: " << input_file.c_str() << endl;
123 cout << "Setting pinconfig file: " << pinconfig_file.c_str() << endl;
124 cout << "Setting custom probe file: " << custom_probe_file.c_str() << endl;
125 cout << "Setting probe type: " << probe_type << endl;
126 cout << "Dump enabled: " << dump_results << endl;
127 cout << "Is Zip file: " << is_zip << endl;
128 if (write_quota != -1) {
129 cout << "Set Write quota: " << write_quota << endl;
130 }
131 }
132
133 PinTool pintool(input_file);
134
135 if (is_zip) {
136 pintool.set_verbose_output(verbose);
137 if (probe_type == ProbeType::CUSTOM) {
138 if (verbose) {
139 cout << "Using custom probe file: " << custom_probe_file << endl;
140 }
141 pintool.read_probe_from_pinlist(custom_probe_file);
142 } else if (probe_type == ProbeType::GENERATE) {
143 if (verbose) {
144 cout << "Generating probe" << endl;
145 }
146 int res = pintool.probe_resident();
147 if (res > 0) {
148 cerr << "Failed to generate probe. Error Code: " << res << endl;
149 return 1;
150 }
151 }
152 pintool.compute_zip_entry_coverages();
153
154 if (pinconfig_file.length() > 0) {
155 // We have provided a pinconfig file so perform filtering
156 // of computed coverages based on it.
157 pintool.filter_zip_entry_coverages(pinconfig_file);
158 }
159
160 if (dump_results) {
161 cout << endl << "----Unfiltered file coverages----" << endl << endl;
162 pintool.dump_coverages(PinTool::DumpType::FILE_COVERAGE);
163
164 if (pinconfig_file.length() > 0) {
165 cout << endl << "----Filtered file coverages----" << endl << endl;
166 pintool.dump_coverages(PinTool::DumpType::FILTERED);
167 }
168 }
169
170 if (output_file.length() > 0) {
171 pintool.write_coverages_as_pinlist(output_file, write_quota);
172 }
173
174 return 0;
175 } else {
176 if (probe_type != ProbeType::GENERATE) {
177 cerr << "Only generating probes is supported for non-zip files, please include "
178 "--gen-probe on your command"
179 << endl;
180 return 1;
181 }
182
183 // Generic file probing will just return resident memory and offsets
184 // without more contextual information.
185 VmaRangeGroup resident;
186
187 int res = pintool.probe_resident();
188 if (res > 0) {
189 cerr << "Failed to generate probe. Error Code: " << res << endl;
190 return 1;
191 }
192
193 pintool.dump_coverages(PinTool::DumpType::PROBE);
194
195 if (output_file.length() > 0) {
196 res = write_pinlist_file(output_file, resident.ranges, write_quota);
197 if (res > 0) {
198 cerr << "Failed to write pin file at: " << output_file << endl;
199 } else if (verbose) {
200 cout << "Finished writing pin file at: " << output_file << endl;
201 }
202 }
203 return res;
204 }
205 return 0;
206 }
207
perform_pinlist_action(const vector<string> & options)208 int perform_pinlist_action(const vector<string>& options) {
209 string pinner_file;
210 bool verbose = false;
211 bool dump = false;
212 bool summary = false;
213
214 if (options.size() < 1) {
215 cerr << "Missing arguments for pinlist mode. See usage for details << endl";
216 return 1;
217 }
218 pinner_file = options[0];
219 for (int i = 1; i < options.size(); ++i) {
220 string option = options[i];
221
222 if (option == "-v") {
223 verbose = true;
224 }
225
226 if (option == "--dump") {
227 dump = true;
228 }
229
230 if (option == "--summary") {
231 summary = true;
232 }
233 }
234
235 if (pinner_file.empty()) {
236 cerr << "Error: Pinlist file to dump is missing. Specify it with '-p <file>'" << endl;
237 return 1;
238 }
239
240 if (verbose) {
241 cout << "Setting file to dump: " << pinner_file.c_str() << endl;
242 }
243
244 vector<VmaRange> vma_ranges;
245 if (read_pinlist_file(pinner_file, vma_ranges) == 1) {
246 cerr << "Failed reading pinlist file" << endl;
247 }
248
249 if (dump) {
250 print_pinlist_ranges(vma_ranges);
251 }
252
253 if (summary) {
254 print_pinlist_summary(vma_ranges);
255 }
256
257 return 0;
258 }
259
print_usage()260 void print_usage() {
261 const string usage = R"(
262 Expected usage: pintool <mode> <required> [option]
263 where:
264 ./pintool <MODE>
265 <MODE>
266 file <filename> [option]
267 [option]
268 --gen-probe
269 Generate a probe from current resident memory based on provided "file"
270 --use-probe <path_to_input_pinlist.meta>
271 Use a previously generated pinlist.meta style file as the probe to match against.
272 --dump
273 Dump output contents to console.
274 --zip
275 Treat the file as a zip/apk file required for doing per-file coverage analysis and generation.
276 --pinconfig <path_to_pinconfig.txt>
277 Filter output coverage ranges using a provided pinconfig.txt style file. See README.md for samples
278 on the format of that file.
279 -v
280 Enable verbose output.
281
282 pinlist <pinlist_file> [option]
283 <pinlist_file>
284 this is the file that will be used for reading and it should follow the pinlist.meta format.
285 [option]
286 --dump
287 Dump <pinlist_file> contents to console output.
288 -v
289 Enable verbose output.
290 --summary
291 Summary results for the pinlist.meta file
292 )";
293 cout << usage.c_str();
294 }
295
main(int argc,char ** argv)296 int main(int argc, char** argv) {
297 if (argc == 1) {
298 print_usage();
299 return 0;
300 }
301
302 if (argc < 2) {
303 cerr << "<mode> is missing";
304 return 1;
305 }
306
307 if (strcmp(argv[1], "--help") == 0) {
308 print_usage();
309 return 0;
310 }
311
312 ToolMode mode = ToolMode::UNKNOWN;
313 if (strcmp(argv[1], "file") == 0) {
314 mode = ToolMode::MAPPED_FILE;
315 } else if (strcmp(argv[1], "pinlist") == 0) {
316 mode = ToolMode::PINLIST;
317 }
318
319 if (mode == ToolMode::UNKNOWN) {
320 cerr << "Failed to find mode: " << argv[1] << ". See usage for available modes." << endl;
321 return 1;
322 }
323
324 vector<string> options;
325 for (int i = 2; i < argc; ++i) {
326 options.push_back(argv[i]);
327 }
328
329 int res;
330 switch (mode) {
331 case ToolMode::MAPPED_FILE:
332 res = perform_file_action(options);
333 break;
334 case ToolMode::PINLIST:
335 res = perform_pinlist_action(options);
336 break;
337 case ToolMode::UNKNOWN:
338 cerr << "Unknown <MODE> see usage for details." << endl;
339 return 1;
340 }
341
342 return res;
343 }
344