summaryrefslogtreecommitdiff
path: root/pinner/pin_utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pinner/pin_utils.cpp')
-rw-r--r--pinner/pin_utils.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/pinner/pin_utils.cpp b/pinner/pin_utils.cpp
new file mode 100644
index 00000000..7c746bb0
--- /dev/null
+++ b/pinner/pin_utils.cpp
@@ -0,0 +1,347 @@
+#include "pin_utils.h"
+#include <android-base/parseint.h>
+#include <algorithm>
+#include <fstream>
+#include <map>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+using namespace std;
+using namespace android::base;
+
+int write_pinlist_file(const std::string& output_file,
+ const std::vector<ZipEntryCoverage>& files_to_write, int64_t write_quota) {
+ std::vector<VmaRange> ranges;
+ for (auto&& file : files_to_write) {
+ ranges.insert(ranges.end(), file.coverage.ranges.begin(), file.coverage.ranges.end());
+ }
+ return write_pinlist_file(output_file, ranges, write_quota);
+}
+
+int write_pinlist_file(const std::string& output_file, const std::vector<VmaRange>& vmas_to_write,
+ int64_t write_quota) {
+ ofstream pinlist_file(output_file);
+ if (pinlist_file.fail()) {
+ return 1;
+ }
+ int64_t total_written = 0;
+ unsigned int page_size = sysconf(_SC_PAGESIZE);
+ const bool has_quota = write_quota > 0;
+ bool reached_quota = false;
+
+ // The PinnerService does not require aligned offsets, however, aligning
+ // allows our summary results to be accurate and avoids over-accounting
+ // of pinning in PinnerService.
+ std::vector<VmaRange> processed_vmas_to_write = vmas_to_write;
+ align_ranges(processed_vmas_to_write, page_size);
+
+ // When we page-align the ranges, we may cause overlaps between ranges
+ // as we elongate the begin offset to match the page the previous
+ // range may end up overlapping the current one.
+ processed_vmas_to_write = merge_ranges(processed_vmas_to_write);
+
+ for (auto&& processed_vma_to_write : processed_vmas_to_write) {
+ uint32_t vma_start_offset = processed_vma_to_write.offset;
+ uint32_t vma_length = processed_vma_to_write.length;
+ if (has_quota && (total_written + vma_length > write_quota)) {
+ // We would go beyond quota, set the maximum allowed write and exit.
+ vma_length = write_quota - total_written;
+ reached_quota = true;
+ }
+ // Transform to BigEndian as PinnerService requires that endianness for reading.
+ uint32_t vma_start_offset_be = htobe32(vma_start_offset);
+ uint32_t vma_length_be = htobe32(vma_length);
+ cout << "Pinlist Writing start=" << vma_start_offset << " bytes=" << vma_length << endl;
+ pinlist_file.write(reinterpret_cast<char*>(&vma_start_offset_be),
+ sizeof(vma_start_offset_be));
+ if (pinlist_file.fail()) {
+ return 1;
+ }
+ pinlist_file.write(reinterpret_cast<char*>(&vma_length_be), sizeof(vma_length_be));
+ total_written += vma_length;
+ if (pinlist_file.fail()) {
+ return 1;
+ }
+
+ if (reached_quota) {
+ break;
+ }
+ }
+ return 0;
+}
+
+int read_pinlist_file(const std::string& pinner_file, /*out*/ std::vector<VmaRange>& pinranges) {
+ ifstream pinlist_file(pinner_file);
+ if (pinlist_file.fail()) {
+ return 1;
+ }
+
+ uint32_t vma_start;
+ uint32_t vma_length;
+ while (!pinlist_file.eof()) {
+ pinlist_file.read(reinterpret_cast<char*>(&vma_start), sizeof(vma_start));
+ pinlist_file.read(reinterpret_cast<char*>(&vma_length), sizeof(vma_length));
+ if (pinlist_file.fail()) {
+ return 1;
+ }
+ vma_start = betoh32(vma_start);
+ vma_length = betoh32(vma_length);
+ pinranges.push_back(VmaRange(vma_start, vma_length));
+ }
+
+ return 0;
+}
+
+ZipEntryCoverage PinConfigFile::to_zipfilemem(const ZipEntryInfo& info) {
+ ZipEntryCoverage file;
+ file.info = info;
+
+ if (ranges.empty()) {
+ cout << "No ranges found for file " << info.name << " creating entire file range" << endl;
+ // Any file coming from pinconfig without explicit
+ // ranges will be assumed to be wanted in its entirety
+ ranges.push_back(VmaRange(0, info.file_size_bytes));
+ }
+
+ file.coverage.ranges = ranges;
+
+ // Offsets specified in pinconfig file are relative to the file
+ // so transform to zip global offsets which are used for coverage
+ // computations.
+ file.coverage.apply_offset(info.offset_in_zip);
+
+ file.coverage.compute_total_size();
+ return file;
+}
+
+int PinConfig::parse(std::string config_file, bool verbose) {
+ ifstream file(config_file);
+ string file_in_zip;
+ if (verbose) {
+ cout << "Parsing file: " << config_file << endl;
+ }
+ string token;
+ file >> token;
+ while (!file.eof()) {
+ if (token == "file") {
+ file >> file_in_zip;
+ PinConfigFile pin_config_file;
+ pin_config_file.filename = file_in_zip;
+ file >> token;
+ while (token != "file" && !file.eof()) {
+ VmaRange range;
+ // Inner parsing loop for per file config.
+ if (token == "offset") {
+ file >> token;
+ android::base::ParseUint(token, &range.offset);
+ file >> token;
+ if (token != "len") {
+ cerr << "Malformed file, expected 'len' after offset" << endl;
+ return 1;
+ }
+ file >> token;
+ android::base::ParseUint(token, &range.length);
+ pin_config_file.ranges.push_back(range);
+ }
+ file >> token;
+ }
+ files_.push_back(pin_config_file);
+ } else {
+ cerr << "Unexpected token: " << token << ". Exit read" << endl;
+ return 1;
+ }
+ }
+
+ if (files_.empty()) {
+ cerr << "Failed parsing pinconfig file, no entries found." << endl;
+ return 1;
+ }
+
+ if (verbose) {
+ cout << "Finished parsing Pinconfig file" << endl;
+ for (auto&& pin_file : files_) {
+ cout << "file=" << pin_file.filename << endl;
+ for (auto&& range : pin_file.ranges) {
+ cout << "offset=" << range.offset << " bytes=" << range.length << endl;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void PinTool::set_custom_zip_inspector(ZipMemInspector* inspector) {
+ delete zip_inspector_;
+ zip_inspector_ = inspector;
+}
+
+void PinTool::set_verbose_output(bool verbose) {
+ verbose_ = verbose;
+}
+
+void PinTool::read_probe_from_pinlist(std::string custom_probe_file) {
+ custom_probe_file_ = custom_probe_file;
+ VmaRangeGroup* custom_probe = new VmaRangeGroup();
+ read_pinlist_file(custom_probe_file_, custom_probe->ranges);
+ custom_probe->compute_total_size();
+ if (custom_probe->ranges.empty()) {
+ cerr << "Did not find any memory range in " << custom_probe_file_ << endl;
+ delete custom_probe;
+ return;
+ }
+ zip_inspector_->set_existing_probe(custom_probe);
+}
+
+int PinTool::probe_resident() {
+ return zip_inspector_->probe_resident();
+}
+
+void PinTool::compute_zip_entry_coverages() {
+ zip_inspector_->compute_per_file_coverage();
+ if (verbose_) {
+ std::vector<ZipEntryInfo> files = zip_inspector_->get_file_infos();
+ for (auto&& file : files) {
+ cout << "file found. name=" << file.name << " offset=" << file.offset_in_zip
+ << " uncompressed=" << file.uncompressed_size
+ << " compressed=" << file.file_size_bytes << endl
+ << endl;
+ }
+ }
+}
+
+void PinTool::dump_coverages(PinTool::DumpType dump_type) {
+ std::vector<ZipEntryCoverage>* file_coverages;
+ if (dump_type == PinTool::DumpType::FILTERED) {
+ file_coverages = &filtered_files_;
+ } else if (dump_type == PinTool::DumpType::FILE_COVERAGE) {
+ file_coverages = &(zip_inspector_->get_file_coverages());
+ } else { // PinTool::DumpType::PROBE
+ VmaRangeGroup* probe = zip_inspector_->get_probe();
+ file_coverages = new vector<ZipEntryCoverage>();
+ ZipEntryCoverage file;
+ file.coverage = *probe;
+ file.info.name = input_file_;
+ file.info.offset_in_zip = 0;
+ uint64_t file_size_bytes = get_file_size(input_file_);
+ if (file_size_bytes == -1) {
+ cerr << "Failed to dump, cannot fstat file: " << input_file_ << endl;
+ delete file_coverages;
+ return;
+ }
+ file.info.file_size_bytes = file_size_bytes;
+ file_coverages->push_back(file);
+ }
+
+ for (auto&& file : *file_coverages) {
+ uint64_t total_size = file.coverage.compute_total_size();
+ cout << file.info.name << " size(B)=" << file.info.file_size_bytes
+ << " resident(B)=" << total_size
+ << " resident(%)=" << (double)(total_size) / file.info.file_size_bytes * 100.0 << endl;
+ if (verbose_) {
+ cout << "file_base_zip_offset=" << file.info.offset_in_zip << endl;
+ }
+ cout << "file resident ranges" << endl;
+ if (dump_type != DumpType::PROBE) {
+ for (auto&& range : file.coverage.ranges) {
+ // The offset in the range represents the absolute absolute offset relative to the
+ // zip so substract the file base offset to get the relative offset within the file
+ // which may be what is worth for a user to specify in pinconfig.txt files.
+ uint64_t offset_in_file = range.offset - file.info.offset_in_zip;
+
+ cout << "zip_offset=" << range.offset << " file_offset=" << offset_in_file
+ << " total_bytes=" << range.length << endl;
+ }
+ } else {
+ for (auto&& range : file.coverage.ranges) {
+ cout << "file_offset=" << range.offset << " total_bytes=" << range.length << endl;
+ }
+ }
+ cout << endl;
+ }
+ cout << endl;
+ if (dump_type == DumpType::PROBE) {
+ // For other dump types we do not create memory, we reuse from class.
+ delete file_coverages;
+ }
+}
+
+void PinTool::filter_zip_entry_coverages(const std::string& pinconfig_filename) {
+ if (pinconfig_filename.length() == 0) {
+ // Nothing to do.
+ return;
+ }
+
+ PinConfig* pinconfig = new PinConfig();
+ if (pinconfig->parse(pinconfig_filename, verbose_) > 0) {
+ cerr << "Failed parsing pinconfig file " << pinconfig_filename << ". Skip filtering";
+ delete pinconfig;
+ return;
+ }
+
+ filter_zip_entry_coverages(pinconfig);
+}
+
+void PinTool::filter_zip_entry_coverages(PinConfig* pinconfig) {
+ pinconfig_ = pinconfig;
+
+ // Filter based on the per file configuration.
+ vector<ZipEntryCoverage> file_coverages = zip_inspector_->get_file_coverages();
+ vector<ZipEntryCoverage>& filtered_files = filtered_files_;
+
+ for (auto&& file_coverage : file_coverages) {
+ for (auto&& pinconfig_file : pinconfig_->files_) {
+ // Match each zip entry against every pattern in filter file.
+ std::string_view file_coverage_view(file_coverage.info.name.c_str());
+ std::string_view pinconfig_view(pinconfig_file.filename.c_str());
+ if (file_coverage_view.find(pinconfig_view) != std::string_view::npos) {
+ // Now that we found a match, create a file with offsets that are global to zip file
+ ZipEntryCoverage file_in_config = pinconfig_file.to_zipfilemem(file_coverage.info);
+ if (verbose_) {
+ cout << "Found a match: file=" << file_coverage.info.name
+ << " matching filter=" << pinconfig_file.filename << endl;
+ for (auto&& range : file_in_config.coverage.ranges) {
+ cout << "zip_offset=" << range.offset << " bytes=" << range.length << endl;
+ }
+ }
+ ZipEntryCoverage filtered_file =
+ file_coverage.compute_coverage(file_in_config.coverage);
+ filtered_files.push_back(filtered_file);
+ break;
+ }
+ }
+ }
+}
+
+std::vector<ZipEntryCoverage> PinTool::get_filtered_zip_entries() {
+ return filtered_files_;
+}
+
+void PinTool::write_coverages_as_pinlist(std::string output_pinlist, int64_t write_quota) {
+ std::vector<ZipEntryCoverage>* pinlist_coverages = nullptr;
+ if (!filtered_files_.empty()) {
+ // Highest preference is writing filtered files if they exist
+ if (verbose_) {
+ cout << "Writing pinconfig filtered file coverages" << endl;
+ }
+ pinlist_coverages = &filtered_files_;
+ } else if (!zip_inspector_->get_file_coverages().empty()) {
+ // Fallback to looking for file coverage computation
+ pinlist_coverages = &zip_inspector_->get_file_coverages();
+ if (verbose_) {
+ cout << "Writing regular file coverages." << endl;
+ }
+ }
+ if (pinlist_coverages == nullptr) {
+ cerr << "Failed to find coverage to write to: " << output_pinlist << endl;
+ return;
+ }
+ int res = write_pinlist_file(output_pinlist, *pinlist_coverages, write_quota);
+ if (res > 0) {
+ cerr << "Failed to write pin file at: " << output_pinlist << endl;
+ } else {
+ if (verbose_) {
+ cout << "Finished writing pin file at: " << output_pinlist << endl;
+ }
+ }
+} \ No newline at end of file