diff options
author | Edgar Arriaga <edgararriaga@google.com> | 2023-08-21 22:00:48 +0000 |
---|---|---|
committer | Edgar Arriaga <edgararriaga@google.com> | 2023-09-07 21:27:03 +0000 |
commit | a66cc9a61ccf58f0c4ccf7238ec8fe0d4eb39bb5 (patch) | |
tree | 6bb95f9d31bab27930b2fbceaaa17b0fd3de012d /pinner | |
parent | b7ebe3b72435044111c8e84400c214a0b060e381 (diff) | |
download | extras-a66cc9a61ccf58f0c4ccf7238ec8fe0d4eb39bb5.tar.gz |
Add pintool and meminspect library
Adding a pintool which can be used to generate pinlist.meta files
used by Android PinnerService and adding a meminspect library which
provides APIs for inspecting resident memory used by the tool to
external systems.
Bug: 297095632
Test: pintool probe -p <file_to_pin>
Test: pintool_tests
Test: meminspect_tests
Change-Id: I0bca42dd29294dd977795d9f30cb3d88deba6bee
Diffstat (limited to 'pinner')
-rw-r--r-- | pinner/Android.bp | 52 | ||||
-rw-r--r-- | pinner/OWNERS | 2 | ||||
-rw-r--r-- | pinner/README.md | 51 | ||||
-rw-r--r-- | pinner/include/meminspect.h | 50 | ||||
-rw-r--r-- | pinner/include/pin_utils.h | 24 | ||||
-rw-r--r-- | pinner/meminspect.cpp | 90 | ||||
-rw-r--r-- | pinner/pin_utils.cpp | 51 | ||||
-rw-r--r-- | pinner/pintool.cpp | 203 | ||||
-rw-r--r-- | pinner/tests/Android.bp | 84 | ||||
-rw-r--r-- | pinner/tests/meminspect_tests.cpp | 105 | ||||
-rw-r--r-- | pinner/tests/pintool_tests.cpp | 28 |
11 files changed, 740 insertions, 0 deletions
diff --git a/pinner/Android.bp b/pinner/Android.bp new file mode 100644 index 00000000..df922e0b --- /dev/null +++ b/pinner/Android.bp @@ -0,0 +1,52 @@ +// +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_library_static { + name: "libmeminspect", + srcs: [ + "meminspect.cpp", + "pin_utils.cpp" + ], + shared_libs: [ + "libbase", + ], + export_include_dirs: ["include"], + cppflags: [ + "-g", + "-Wall", + "-Werror", + ], +} + +cc_binary { + name: "pintool", + srcs: ["pintool.cpp"], + shared_libs: [ + "libbase", + ], + static_libs: [ + "libmeminspect", + ], + cppflags: [ + "-g", + "-Wall", + "-Werror", + ], +} diff --git a/pinner/OWNERS b/pinner/OWNERS new file mode 100644 index 00000000..0636f908 --- /dev/null +++ b/pinner/OWNERS @@ -0,0 +1,2 @@ +edgararriaga@google.com +shayba@google.com
\ No newline at end of file diff --git a/pinner/README.md b/pinner/README.md new file mode 100644 index 00000000..f7b37f88 --- /dev/null +++ b/pinner/README.md @@ -0,0 +1,51 @@ +# Pin Tool + +This tool is used for multiple operations related to memory pinning and inspection. + +## Build and Install the tool + +To build and push the binary to device +``` +mm pintool +and push $ANDROID_REPO/out/target/product/<lunchtarget>/system/bin/pintool /data/local/tmp/pintool +adb shell +cd /data/local/tmp/pintool +``` + +## How to use the tool to generate pinner files + +One of the core usages of the tool is to inspect resident memory and +generate pinlist.meta files which are then consumed by the Android PinnerService +to pin memory (mlock) and avoid it from being evicted. + +A sample usage of this tool in a PGO style fashion can be like this: + + +This is a sample flow of how to use this tool: +1. Run the app that makes use of the file you want to pin. To have resident +memory for the file. + +2. Run this program to generate the pinlist.meta file. +``` +./pintool probe -p <file_to_probe> <options> +``` +This will inspect the memory for the provided <file_to_probe> and generate +a pinlist.meta file with the resident memory ranges. + +Note: Running ./pintool to obtain more usage documentation. + +3. Output "pinlist.meta" can be incorporate it within your build +process to have it bundled inside your apk (inside assets/<file_to_pin.apk>) +to have the PinnerService know what memory ranges to pin within your apk. +Note: The PinnerService will need to support pinning your apk, so you may +need to explicitly request a pin. + +## Other potential uses + +Outside of pinner service, the tool can be used to inspect resident memory for +any file in memory. + +## Extra information + +the pinlist.meta depends on the apk contents and needs to be regenrated if +you are pushing a new version of your apk.
\ No newline at end of file diff --git a/pinner/include/meminspect.h b/pinner/include/meminspect.h new file mode 100644 index 00000000..11cca2ba --- /dev/null +++ b/pinner/include/meminspect.h @@ -0,0 +1,50 @@ +#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> + +#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(uint32_t off, uint32_t len) : offset(off), length(len) {} +}; + +struct ResidentMemResult { + public: + std::vector<VmaRange> resident_memory_ranges; + uint64_t file_size_bytes; + uint64_t total_resident_bytes; +}; + +/** + * @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 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, ResidentMemResult& out_resident_mem, + int pages_per_mincore = DEFAULT_PAGES_PER_MINCORE);
\ No newline at end of file diff --git a/pinner/include/pin_utils.h b/pinner/include/pin_utils.h new file mode 100644 index 00000000..8db0e8aa --- /dev/null +++ b/pinner/include/pin_utils.h @@ -0,0 +1,24 @@ +#pragma once + +#include "meminspect.h" + +/** + * @brief Generate a pinlist file from a given list of vmas containing a list of 4-byte pairs + * representing (4-byte offset, 4-byte len) contiguous in memory and they are stored in big endian + * format. + * + * @param output_file Output file to write pinlist + * @param vmas_to_pin Set of vmas to write into pinlist file. + * @return 0 on success, non-zero on failure + */ +int write_pinlist_file(const std::string& output_file, const std::vector<VmaRange>& vmas_to_pin); + +/** + * @brief This method is the counter part of @see write_pinlist_file(). It will read an existing + * pinlist file. + * + * @param pinner_file Input pinlist file + * @param pinranges Vmas read from pinlist file. This is populated on call. + * @return 0 on success, non-zero on failure + */ +int read_pinlist_file(const std::string& pinner_file, /*out*/ std::vector<VmaRange>& pinranges);
\ No newline at end of file diff --git a/pinner/meminspect.cpp b/pinner/meminspect.cpp new file mode 100644 index 00000000..181129ad --- /dev/null +++ b/pinner/meminspect.cpp @@ -0,0 +1,90 @@ +#include "meminspect.h" +#include <android-base/unique_fd.h> + +using namespace std; +using namespace android::base; +using namespace ::android::base; + +int probe_resident_memory(string probed_file, + /*out*/ ResidentMemResult& memresult, int pages_per_mincore) { + unique_fd probed_file_ufd(open(probed_file.c_str(), O_RDONLY)); + int probe_fd = probed_file_ufd.get(); + if (probe_fd == -1) { + return MEMINSPECT_FAIL_OPEN; + } + + struct stat fstat_res; + int res = fstat(probe_fd, &fstat_res); + if (res == -1) { + return MEMINSPECT_FAIL_FSTAT; + } + unsigned long total_bytes = fstat_res.st_size; + + memresult.file_size_bytes = total_bytes; + + char* base_address = (char*)mmap(0, total_bytes, PROT_READ, MAP_SHARED, probe_fd, /*offset*/ 0); + + // this determines how many pages to inspect per mincore syscall + unsigned char* window = new unsigned char[pages_per_mincore]; + + unsigned int page_size = sysconf(_SC_PAGESIZE); + unsigned long bytes_inspected = 0; + unsigned long pages_in_memory = 0; + + // total bytes in inspection window + unsigned long window_bytes = page_size * pages_per_mincore; + + char* current_window_address; + bool started_vma_range = false; + uint32_t resident_vma_start_offset = 0; + for (current_window_address = base_address; bytes_inspected < total_bytes; + current_window_address += window_bytes, bytes_inspected += window_bytes) { + int res = mincore(current_window_address, window_bytes, window); + if (res != 0) { + if (errno == ENOMEM) { + // Did not find page, maybe it's a hole. + continue; + } + return MEMINSPECT_FAIL_MINCORE; + } + // Inspect the provided mincore window result sequentially + // and as soon as a change in residency happens a range is + // created or finished. + for (int iWin = 0; iWin < pages_per_mincore; ++iWin) { + if ((window[iWin] & 1) > 0) { + // Page is resident + ++pages_in_memory; + if (!started_vma_range) { + // End of range + started_vma_range = true; + uint32_t window_offset = iWin * page_size; + resident_vma_start_offset = + current_window_address + window_offset - base_address; + } + } else { + // Page is not resident + if (started_vma_range) { + // Start of range + started_vma_range = false; + uint32_t window_offset = iWin * page_size; + uint32_t resident_vma_end_offset = + current_window_address + window_offset - base_address; + uint32_t resident_len = resident_vma_end_offset - resident_vma_start_offset; + VmaRange vma_range(resident_vma_start_offset, resident_len); + memresult.resident_memory_ranges.push_back(vma_range); + } + } + } + } + // This was the last window, so close any opened vma range + if (started_vma_range) { + started_vma_range = false; + uint32_t in_memory_vma_end = current_window_address - base_address; + uint32_t resident_len = in_memory_vma_end - resident_vma_start_offset; + VmaRange vma_range(resident_vma_start_offset, resident_len); + memresult.resident_memory_ranges.push_back(vma_range); + } + + memresult.total_resident_bytes = pages_in_memory * page_size; + return 0; +}
\ No newline at end of file diff --git a/pinner/pin_utils.cpp b/pinner/pin_utils.cpp new file mode 100644 index 00000000..4469d781 --- /dev/null +++ b/pinner/pin_utils.cpp @@ -0,0 +1,51 @@ +#include "pin_utils.h" + +using namespace std; +using namespace android::base; + +int write_pinlist_file(const std::string& output_file, const std::vector<VmaRange>& vmas_to_pin) { + int pinlist_fd = open(output_file.c_str(), O_RDWR | O_CREAT, "w"); + if (pinlist_fd == -1) { + return 1; + } + for (int i = 0; i < vmas_to_pin.size(); ++i) { + uint32_t vma_start_offset = vmas_to_pin[i].offset; + uint32_t vma_length = vmas_to_pin[i].length; + // 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); + int write_res = write(pinlist_fd, &vma_start_offset_be, sizeof(vma_start_offset_be)); + if (write_res == -1) { + return 1; + } + write_res = write(pinlist_fd, &vma_length_be, sizeof(vma_length_be)); + if (write_res == -1) { + return 1; + } + } + close(pinlist_fd); + return 0; +} + +int read_pinlist_file(const std::string& pinner_file, /*out*/ std::vector<VmaRange>& pinranges) { + int pinlist_fd = open(pinner_file.c_str(), O_RDWR | O_CREAT, "w"); + if (pinlist_fd == -1) { + return 1; + } + + uint32_t vma_start; + uint32_t vma_length; + while (read(pinlist_fd, &vma_start, sizeof(vma_start)) > 0) { + int read_res = read(pinlist_fd, &vma_length, sizeof(vma_length)); + if (read_res == -1) { + return 1; + } + vma_start = betoh32(vma_start); + vma_length = betoh32(vma_length); + + pinranges.push_back(VmaRange(vma_start, vma_length)); + } + + close(pinlist_fd); + return 0; +}
\ No newline at end of file diff --git a/pinner/pintool.cpp b/pinner/pintool.cpp new file mode 100644 index 00000000..a25106b9 --- /dev/null +++ b/pinner/pintool.cpp @@ -0,0 +1,203 @@ +#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 <meminspect.h> +#include <pin_utils.h> + +using namespace std; +using namespace android::base; + +enum ToolMode { PROBE, DUMP, UNKNOWN }; + +void print_pinner_ranges(const std::vector<VmaRange>& ranges) { + cout << "vmas to pin:" << endl; + for (int i = 0; i < ranges.size(); ++i) { + cout << StringPrintf("start=%u length=%u", ranges[i].offset, ranges[i].length) << endl; + } +} + +int perform_probe(const vector<string>& options) { + std::string probed_file; + std::string output_file = "/data/local/tmp/pinlist.meta"; + bool verbose = false; + int pages_per_mincore = DEFAULT_PAGES_PER_MINCORE; + + // Parse Args + for (int i = 0; i < options.size(); ++i) { + string option = options[i]; + if (option == "-p") { + ++i; + probed_file = options[i]; + continue; + } + if (option == "-o") { + ++i; + output_file = options[i]; + continue; + } + if (option == "-v") { + verbose = true; + continue; + } + if (option == "-w") { + ++i; + pages_per_mincore = stoi(options[i]); + continue; + } + } + + if (verbose) { + cout << "mincore window size=" << pages_per_mincore << endl; + cout << "Setting output pinlist file as " << probed_file.c_str() << endl; + cout << "Setting file to probe: " << probed_file.c_str() << endl; + } + + if (probed_file.empty()) { + cerr << "Error: Should specify a file to probe."; + return 1; + } + + ResidentMemResult memresult; + int res = probe_resident_memory(probed_file, memresult, pages_per_mincore); + if (res) { + if (res == MEMINSPECT_FAIL_OPEN) { + cerr << "Failed to open file: " << probed_file << endl; + } + + if (res == MEMINSPECT_FAIL_FSTAT) { + cerr << "Failed to fstat file: " << probed_file << endl; + } + + if (res == MEMINSPECT_FAIL_MINCORE) { + cerr << "Mincore failed for file: " << probed_file << endl; + } + + return res; + } + + cout << StringPrintf( + "Finished Probing. resident memory(KB)=%lu. file_size (KB)=%lu. " + "pin_percentage=%f", + memresult.total_resident_bytes / 1024, memresult.file_size_bytes / 1024, + memresult.total_resident_bytes / (float)memresult.file_size_bytes * 100) + << endl; + + res = write_pinlist_file(output_file, memresult.resident_memory_ranges); + if (res > 0) { + cerr << "Failed to write pin file at: " << output_file << endl; + } else { + if (verbose) { + cout << "Finished writing pin file at: " << output_file << endl; + } + } + return res; +} + +int perform_dump(const vector<string>& options) { + string pinner_file; + bool verbose = false; + for (int i = 0; i < options.size(); ++i) { + string option = options[i]; + if (option == "-p") { + ++i; + pinner_file = options[i]; + continue; + } + + if (option == "-v") { + verbose = true; + } + } + if (pinner_file.empty()) { + cerr << "Error: Pinlist file to dump is missing. Specify it with '-p <file>'" << endl; + return 1; + } + if (verbose) { + cout << "Setting file to dump: " << pinner_file.c_str() << endl; + } + vector<VmaRange> vma_ranges; + if (read_pinlist_file(pinner_file, vma_ranges) == 1) { + cerr << "Failed reading pinlist file" << endl; + } + print_pinner_ranges(vma_ranges); + + return 0; +} + +int main(int argc, char** argv) { + if (argc == 1) { + const string usage = R"( +Expected usage: pintool <mode> <required> [option] +where: +<file_to_pin> is a file currently mapped by another process and in memory. +<mode> : + probe + This mode will probe resident memory for a file and generate a pinlist.meta file + that can be interpreted by the PinnerService. + + <required> + -p <file_to_probe> + This option will probe the specified file + [option]: + -o <file> + Specify the output file for the pinlist file. + (default=/data/local/tmp/pinlist.meta) + -v + Enable verbose output. + -w + Mincore total pages per mincore window. Bigger windows + will use more memory but may be slightly faster. (default=1) + dump + <required> + -p <input_pinlist_file> + Specify the input pinlist file to dump +)"; + + cout << usage.c_str(); + return 0; + } + + if (argc < 2) { + cerr << "<mode> is missing"; + return 1; + } + ToolMode mode = UNKNOWN; + if (strcmp(argv[1], "probe") == 0) { + mode = PROBE; + } else if (strcmp(argv[1], "dump") == 0) { + mode = DUMP; + } + + if (mode == UNKNOWN) { + cerr << "Failed to find mode: " << argv[1] << ". See usage for available modes." << endl; + return 1; + } + + vector<string> options; + for (int i = 2; i < argc; ++i) { + options.push_back(argv[i]); + } + + int res; + switch (mode) { + case PROBE: + res = perform_probe(options); + break; + case DUMP: + res = perform_dump(options); + break; + case UNKNOWN: + return 1; + break; + } + + return res; +} diff --git a/pinner/tests/Android.bp b/pinner/tests/Android.bp new file mode 100644 index 00000000..8c3f262a --- /dev/null +++ b/pinner/tests/Android.bp @@ -0,0 +1,84 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "meminspect_tests", + shared_libs: [ + "libbase", + ], + + cppflags: [ + "-g", + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + ], + + static_libs: [ + "libmeminspect", + ], + + target: { + android: { + srcs: ["meminspect_tests.cpp"], + }, + }, + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-O0", + ], + + compile_multilib: "first", +} + +cc_test { + name: "pintool_tests", + + shared_libs: [ + "libbase", + ], + + cppflags: [ + "-g", + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + ], + + static_libs: [ + "libmeminspect", + ], + + target: { + android: { + srcs: ["pintool_tests.cpp"], + }, + }, + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-O0", + ], + + compile_multilib: "first", +} diff --git a/pinner/tests/meminspect_tests.cpp b/pinner/tests/meminspect_tests.cpp new file mode 100644 index 00000000..da3826db --- /dev/null +++ b/pinner/tests/meminspect_tests.cpp @@ -0,0 +1,105 @@ +#include <gtest/gtest.h> +#include <stdio.h> + +#include <meminspect.h> + +using namespace std; + +/** + * This test is meant to be ran by directly pushing the test binary + * into the device as using atest will not provide sufficient privileges + * to execute drop_caches command. + */ +TEST(meminspect_test, inspect_matches_resident) { + // Create test file + string test_file = "/data/local/tmp/meminspect_test"; + // If for any reason a test file already existed from a previous test, remove it. + remove(test_file.c_str()); + + int test_file_fd = open(test_file.c_str(), O_RDWR | O_CREAT, "w"); + unsigned int page_size = sysconf(_SC_PAGESIZE); + if (test_file_fd == -1) { + ADD_FAILURE() << "Failed to open test file for writing. errno: " << std::strerror(errno); + close(test_file_fd); + remove(test_file.c_str()); + return; + } + + uint8_t* page_data = new uint8_t[page_size]; + for (unsigned int i = 0; i < page_size; ++i) { + page_data[i] = i + 1; + } + int pages_to_write = 100; + for (int page = 0; page < pages_to_write; ++page) { + write(test_file_fd, page_data, page_size); + } + // fsync to ensure the data is flushed to disk. + if (fsync(test_file_fd) == -1) { + ADD_FAILURE() << "fsync failed errno: " << std::strerror(errno); + close(test_file_fd); + remove(test_file.c_str()); + return; + } + close(test_file_fd); + + // Drop the pagecache to ensure we do not have memory due to it staying there after write. + int drop_cache_fd = open("/proc/sys/vm/drop_caches", O_WRONLY); + if (drop_cache_fd == -1) { + ADD_FAILURE() << "failed opening drop caches fd errno: " << std::strerror(errno); + close(test_file_fd); + remove(test_file.c_str()); + return; + } + write(drop_cache_fd, "3", 1); + fsync(drop_cache_fd); + close(drop_cache_fd); + + // Open file and page in some memory + test_file_fd = open(test_file.c_str(), O_RDONLY, "r"); + if (test_file_fd == -1) { + ADD_FAILURE() << "Failed to open test file for reading after creation. errno: " + << std::strerror(errno); + close(test_file_fd); + remove(test_file.c_str()); + return; + } + + char* base_address = (char*)mmap(0, page_size * pages_to_write, PROT_READ, MAP_SHARED, + test_file_fd, /*offset*/ 0); + if (base_address == (char*)-1) { + ADD_FAILURE() << "Failed to mmap file for reading after creation. errno: " + << std::strerror(errno); + close(test_file_fd); + remove(test_file.c_str()); + return; + } + + ResidentMemResult vmas_resident; + int res = probe_resident_memory(test_file, vmas_resident, 1); + EXPECT_TRUE(res == 0); + + // Probing the file without reading anything yields no resident memory + EXPECT_TRUE(vmas_resident.resident_memory_ranges.empty()); + + // Clear our to start fresh for next probe. + vmas_resident = ResidentMemResult(); + + int pages_to_read = 1; + char* read_data = new char[pages_to_read]; + for (int page = 0; page < pages_to_read; ++page) { + // Read 1 byte from each page to page it in. + read_data[page] = base_address[page * page_size]; + } + res = probe_resident_memory(test_file, vmas_resident, 1); + EXPECT_TRUE(res == 0); + + // The amount of memory paged in is outside our control, but we should have some. + EXPECT_TRUE(vmas_resident.total_resident_bytes > 0); + EXPECT_TRUE(vmas_resident.resident_memory_ranges.size() == 1); + EXPECT_TRUE(vmas_resident.resident_memory_ranges[0].offset == 0); + EXPECT_TRUE((uint64_t)vmas_resident.resident_memory_ranges[0].length == + vmas_resident.total_resident_bytes); + + close(test_file_fd); + remove(test_file.c_str()); +}
\ No newline at end of file diff --git a/pinner/tests/pintool_tests.cpp b/pinner/tests/pintool_tests.cpp new file mode 100644 index 00000000..b76c5284 --- /dev/null +++ b/pinner/tests/pintool_tests.cpp @@ -0,0 +1,28 @@ +#include <gtest/gtest.h> +#include <stdio.h> + +#include <pin_utils.h> + +using namespace std; + +TEST(pintool_test, pinlist_matches_memranges) { + vector<VmaRange> vma_ranges; + vma_ranges.push_back(VmaRange(0, 500)); + vma_ranges.push_back(VmaRange(1000, 5500)); + vma_ranges.push_back(VmaRange(10000, 13000)); + vma_ranges.push_back(VmaRange(26000, 35000)); + + string test_file = "/data/local/tmp/pintool_test"; + write_pinlist_file(test_file, vma_ranges); + + vector<VmaRange> read_ranges; + read_pinlist_file(test_file, read_ranges); + + EXPECT_EQ(vma_ranges.size(), read_ranges.size()); + for (size_t i = 0; i < vma_ranges.size(); ++i) { + EXPECT_EQ(vma_ranges[i].offset, read_ranges[i].offset); + EXPECT_EQ(vma_ranges[i].length, read_ranges[i].length); + } + + remove(test_file.c_str()); +}
\ No newline at end of file |