| #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; |
| } |
| |
| VmaRangeGroup 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.ranges.empty()); |
| |
| // Clear our to start fresh for next probe. |
| vmas_resident = VmaRangeGroup(); |
| |
| 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. |
| uint64_t resident_total_size = vmas_resident.compute_total_size(); |
| EXPECT_TRUE(resident_total_size > 0); |
| EXPECT_TRUE(vmas_resident.ranges.size() == 1); |
| EXPECT_TRUE(vmas_resident.ranges[0].offset == 0); |
| EXPECT_TRUE((uint64_t)vmas_resident.ranges[0].length == resident_total_size); |
| |
| close(test_file_fd); |
| remove(test_file.c_str()); |
| } |
| |
| TEST(meminspect_test, custom_probe_coverage_matches_with_probe) { |
| ZipMemInspector inspector(""); |
| VmaRangeGroup* probe = new VmaRangeGroup(); |
| probe->ranges.push_back(VmaRange(0, 500)); |
| probe->ranges.push_back(VmaRange(700, 100)); |
| probe->ranges.push_back(VmaRange(1000, 500)); |
| probe->ranges.push_back(VmaRange(2000, 100)); |
| // Probed Resident Memory Offset ranges: |
| // [0,500],[700,800],[1000,1500],[2000,2100] |
| EXPECT_EQ(probe->compute_total_size(), (unsigned long long)1200); |
| inspector.set_existing_probe(probe); |
| |
| // Emulate reading some files from the zip to compute their coverages |
| // fake1 memory offset ranges [100,300] |
| ZipEntryInfo info; |
| info.name = "fake1"; |
| info.offset_in_zip = 100; |
| info.file_size_bytes = 200; |
| inspector.add_file_info(info); |
| |
| // fake2 memory offset ranges [600,1200] |
| ZipEntryInfo info2; |
| info2.name = "fake2"; |
| info2.offset_in_zip = 600; |
| info2.file_size_bytes = 600; |
| inspector.add_file_info(info2); |
| |
| inspector.compute_per_file_coverage(); |
| std::vector<ZipEntryCoverage> coverages = inspector.get_file_coverages(); |
| EXPECT_EQ(coverages.size(), (size_t)2); |
| |
| // Result coverage for fake1 should be: [100,300] |
| EXPECT_EQ(coverages[0].coverage.ranges[0].offset, (uint32_t)100); |
| EXPECT_EQ(coverages[0].coverage.ranges[0].length, (uint32_t)200); |
| EXPECT_EQ(coverages[0].coverage.compute_total_size(), (unsigned long long)200); |
| EXPECT_EQ(coverages[0].info.name, "fake1"); |
| EXPECT_EQ(coverages[0].info.offset_in_zip, (uint32_t)100); |
| EXPECT_EQ(coverages[0].info.file_size_bytes, (uint32_t)200); |
| |
| // coverage coverage for fake2 should be: [700,800] and [1000,1200] |
| EXPECT_EQ(coverages[1].coverage.ranges[0].offset, (uint32_t)700); |
| EXPECT_EQ(coverages[1].coverage.ranges[0].length, (uint32_t)100); |
| EXPECT_EQ(coverages[1].coverage.ranges[1].offset, (uint32_t)1000); |
| EXPECT_EQ(coverages[1].coverage.ranges[1].length, (uint32_t)200); |
| EXPECT_EQ(coverages[1].coverage.compute_total_size(), (unsigned long long)300); // 100 + |
| // 200 |
| EXPECT_EQ(coverages[1].info.name, "fake2"); |
| EXPECT_EQ(coverages[1].info.offset_in_zip, (uint32_t)600); |
| EXPECT_EQ(coverages[1].info.file_size_bytes, (uint32_t)600); |
| } |
| |
| TEST(meminspect_test, whole_file_coverage_against_probe) { |
| ZipMemInspector inspector(""); |
| |
| // Emulate reading some files from the zip to compute their coverages |
| // fake1 memory offset ranges [100,300] |
| ZipEntryInfo info; |
| info.name = "fake1"; |
| info.offset_in_zip = 100; |
| info.file_size_bytes = 200; |
| inspector.add_file_info(info); |
| |
| // fake2 memory offset ranges [600,1200] |
| ZipEntryInfo info2; |
| info2.name = "fake2"; |
| info2.offset_in_zip = 600; |
| info2.file_size_bytes = 600; |
| inspector.add_file_info(info2); |
| |
| inspector.compute_per_file_coverage(); |
| std::vector<ZipEntryCoverage> coverages = inspector.get_file_coverages(); |
| EXPECT_EQ(coverages.size(), (size_t)2); |
| |
| // Check that coverage matches entire file sizes |
| EXPECT_EQ(coverages[0].coverage.ranges[0].offset, (uint32_t)100); |
| EXPECT_EQ(coverages[0].coverage.ranges[0].length, (uint32_t)200); |
| EXPECT_EQ(coverages[0].coverage.compute_total_size(), (unsigned long long)200); |
| EXPECT_EQ(coverages[0].info.name, "fake1"); |
| EXPECT_EQ(coverages[0].info.offset_in_zip, (uint32_t)100); |
| EXPECT_EQ(coverages[0].info.file_size_bytes, (uint32_t)200); |
| |
| EXPECT_EQ(coverages[1].coverage.ranges[0].offset, (uint32_t)600); |
| EXPECT_EQ(coverages[1].coverage.ranges[0].length, (uint32_t)600); |
| EXPECT_EQ(coverages[1].coverage.compute_total_size(), (unsigned long long)600); |
| EXPECT_EQ(coverages[1].info.name, "fake2"); |
| EXPECT_EQ(coverages[1].info.offset_in_zip, (uint32_t)600); |
| EXPECT_EQ(coverages[1].info.file_size_bytes, (uint32_t)600); |
| } |
| |
| TEST(meminspect_test, file_multiple_ranges_matches_probe) { |
| VmaRangeGroup probe; |
| probe.ranges.push_back(VmaRange(0, 500)); |
| probe.ranges.push_back(VmaRange(700, 100)); |
| probe.ranges.push_back(VmaRange(1000, 500)); |
| probe.ranges.push_back(VmaRange(2000, 100)); |
| // Probed Resident Memory Offset ranges: |
| // [0,500],[700,800],[1000,1500],[2000,2100] |
| EXPECT_EQ(probe.compute_total_size(), (unsigned long long)1200); |
| |
| std::vector<ZipEntryCoverage> desired_coverages; |
| |
| // fake1 file resides between [100,1100] |
| // desired ranges are [100,200],[400,710],[820,850] |
| ZipEntryCoverage file1_mem; |
| file1_mem.info.name = "fake1"; |
| file1_mem.info.offset_in_zip = 100; |
| file1_mem.info.file_size_bytes = 1000; |
| file1_mem.coverage.ranges.push_back(VmaRange(100, 100)); |
| file1_mem.coverage.ranges.push_back(VmaRange(400, 310)); |
| file1_mem.coverage.ranges.push_back(VmaRange(820, 30)); |
| desired_coverages.push_back(file1_mem); |
| |
| // fake2 memory offset ranges [1300,2100] |
| // desired ranges are [1400,1500],[1600,1650],[1800,2050] |
| ZipEntryCoverage file2_mem; |
| file2_mem.info.name = "fake2"; |
| file2_mem.info.offset_in_zip = 1300; |
| file2_mem.info.file_size_bytes = 750; |
| file2_mem.coverage.ranges.push_back(VmaRange(1400, 100)); |
| file2_mem.coverage.ranges.push_back(VmaRange(1600, 50)); |
| file2_mem.coverage.ranges.push_back(VmaRange(1800, 250)); |
| desired_coverages.push_back(file2_mem); |
| |
| std::vector<ZipEntryCoverage> coverages = |
| ZipMemInspector::compute_coverage(desired_coverages, &probe); |
| |
| EXPECT_EQ(coverages.size(), (size_t)2); |
| |
| // Result coverage for fake1 should be: [100,200],[400,500],[700,710] |
| EXPECT_EQ(coverages[0].coverage.ranges[0].offset, (uint32_t)100); |
| EXPECT_EQ(coverages[0].coverage.ranges[0].length, (uint32_t)100); |
| EXPECT_EQ(coverages[0].coverage.ranges[1].offset, (uint32_t)400); |
| EXPECT_EQ(coverages[0].coverage.ranges[1].length, (uint32_t)100); |
| EXPECT_EQ(coverages[0].coverage.ranges[2].offset, (uint32_t)700); |
| EXPECT_EQ(coverages[0].coverage.ranges[2].length, (uint32_t)10); |
| |
| EXPECT_EQ(coverages[0].coverage.compute_total_size(), (unsigned long long)210); |
| EXPECT_EQ(coverages[0].info.name, "fake1"); |
| EXPECT_EQ(coverages[0].info.offset_in_zip, (uint32_t)100); |
| EXPECT_EQ(coverages[0].info.file_size_bytes, (uint32_t)1000); |
| |
| // coverage coverage for fake2 should be: [1400,1500],[2000,2050] |
| EXPECT_EQ(coverages[1].coverage.ranges[0].offset, (uint32_t)1400); |
| EXPECT_EQ(coverages[1].coverage.ranges[0].length, (uint32_t)100); |
| EXPECT_EQ(coverages[1].coverage.ranges[1].offset, (uint32_t)2000); |
| EXPECT_EQ(coverages[1].coverage.ranges[1].length, (uint32_t)50); |
| EXPECT_EQ(coverages[1].coverage.compute_total_size(), (unsigned long long)150); |
| EXPECT_EQ(coverages[1].info.name, "fake2"); |
| EXPECT_EQ(coverages[1].info.offset_in_zip, (uint32_t)1300); |
| EXPECT_EQ(coverages[1].info.file_size_bytes, (uint32_t)750); |
| } |
| |
| TEST(meminspect_test, range_alignment_and_merge_matches) { |
| ZipMemInspector inspector(""); |
| VmaRangeGroup* probe = new VmaRangeGroup(); |
| probe->ranges.push_back(VmaRange(0, 500)); |
| probe->ranges.push_back(VmaRange(700, 100)); |
| int page_size = 4096; |
| |
| // Probed Resident Memory Offset ranges: |
| // [0,500],[700,800] |
| inspector.set_existing_probe(probe); |
| |
| // When we page align, we should end up with [0,500],[0,800] |
| align_ranges(probe->ranges, page_size); |
| EXPECT_EQ(probe->ranges[0].offset, (uint32_t)0); |
| EXPECT_EQ(probe->ranges[0].length, (uint32_t)500); |
| EXPECT_EQ(probe->ranges[1].offset, (uint32_t)0); |
| EXPECT_EQ(probe->ranges[1].length, (uint32_t)800); |
| EXPECT_EQ(probe->ranges.size(), (uint32_t)2); |
| |
| // Because we have overlapping ranges, a union-merge should |
| // skip duplication of intersections and end up with [0,800] |
| std::vector<VmaRange> merged = merge_ranges(probe->ranges); |
| EXPECT_EQ(merged[0].offset, (uint32_t)0); |
| EXPECT_EQ(merged[0].length, (uint32_t)800); |
| EXPECT_EQ(merged.size(), (uint32_t)1); |
| } |