xref: /aosp_15_r20/system/extras/pinner/pintool.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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