diff --git a/benchmarks/suites/syscall.xml b/benchmarks/suites/syscall.xml index c253a3fc8..9f0afb080 100644 --- a/benchmarks/suites/syscall.xml +++ b/benchmarks/suites/syscall.xml @@ -50,3 +50,44 @@ AT_MULTI_PAGE_SIZES + + + BM_syscall_mmap_anon_mprotect_rw_to_rd + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_anon_mprotect_rw_to_none + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_anon_mprotect_rd_to_none + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_file_mprotect_rw_to_rd + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_file_mprotect_rw_to_none + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_file_mprotect_none_to_rw + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_file_mprotect_none_to_rd + 10 + AT_All_PAGE_SIZES + + + BM_syscall_mmap_file_mprotect_rd_to_none + 10 + AT_All_PAGE_SIZES + diff --git a/benchmarks/syscall_mm_benchmark.cpp b/benchmarks/syscall_mm_benchmark.cpp index 34ffa2ea7..04c4beadc 100644 --- a/benchmarks/syscall_mm_benchmark.cpp +++ b/benchmarks/syscall_mm_benchmark.cpp @@ -38,6 +38,12 @@ struct MmapParams { int64_t size; }; +struct MprotectParams { + int from_prot; + int to_prot; + int64_t size; +}; + template void MmapBenchmarkImpl(benchmark::State& state, const struct MmapParams& params, int fd, void* area = nullptr) { @@ -307,3 +313,152 @@ static void BM_syscall_mmap_anon_madvise_free(benchmark::State& state) { MadviseBenchmark(state, params, MADV_FREE); } BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_madvise_free, "AT_MULTI_PAGE_SIZES"); + +void MprotectBenchmark(benchmark::State& state, const struct MprotectParams& params, void* addr) { + for (auto _ : state) { + state.PauseTiming(); + /* + * Guarantee that physical memory pages are allocated for this region to prevent + * segmentation fault when using mprotect to change permissions. + */ + if (params.from_prot & PROT_WRITE) { + MakeAllocationResident(addr, params.size, page_sz); + } + state.ResumeTiming(); + + if (mprotect(addr, params.size, params.to_prot) != 0) { + state.SkipWithError(android::base::StringPrintf("mprotect failed: %m")); + break; + } + + state.PauseTiming(); + // Revert back to the original protection + int res = mprotect(addr, params.size, params.from_prot); + state.ResumeTiming(); + if (res != 0) { + state.SkipWithError( + android::base::StringPrintf("mprotect failed to revert to original prot: %m")); + break; + } + } +} + +static void MprotectBenchmarkWithMmapAnon(benchmark::State& state, + const struct MprotectParams& params) { + void* addr = mmap(nullptr, params.size, params.from_prot, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (addr == MAP_FAILED) { + state.SkipWithError(android::base::StringPrintf("mmap failed: %m")); + return; + } + + MprotectBenchmark(state, params, addr); + + if (munmap(addr, params.size) != 0) + state.SkipWithError(android::base::StringPrintf("munmap failed: %m")); +} + +static void BM_syscall_mmap_anon_mprotect_rw_to_rd(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_READ | PROT_WRITE, + .to_prot = PROT_READ, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapAnon(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_mprotect_rw_to_rd, "AT_All_PAGE_SIZES"); + +static void BM_syscall_mmap_anon_mprotect_rw_to_none(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_READ | PROT_WRITE, + .to_prot = PROT_NONE, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapAnon(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_mprotect_rw_to_none, "AT_All_PAGE_SIZES"); + +static void BM_syscall_mmap_anon_mprotect_rd_to_none(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_READ, + .to_prot = PROT_NONE, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapAnon(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_mprotect_rd_to_none, "AT_All_PAGE_SIZES"); + +static void MprotectBenchmarkWithMmapFile(benchmark::State& state, + const struct MprotectParams& params) { + TemporaryFile tf; + + if (tf.fd < 0) { + state.SkipWithError(android::base::StringPrintf("failed to create a temporary file: %m")); + return; + } + + if (params.size > 0 && ftruncate(tf.fd, params.size)) { + state.SkipWithError(android::base::StringPrintf("ftruncate failed: %m")); + return; + } + + void* addr = mmap(nullptr, params.size, params.from_prot, MAP_PRIVATE, tf.fd, 0); + if (addr == MAP_FAILED) { + state.SkipWithError(android::base::StringPrintf("mmap failed: %m")); + return; + } + + MprotectBenchmark(state, params, addr); + + if (munmap(addr, params.size) != 0) + state.SkipWithError(android::base::StringPrintf("munmap failed: %m")); +} + +static void BM_syscall_mmap_file_mprotect_rw_to_rd(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_READ | PROT_WRITE, + .to_prot = PROT_READ, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapFile(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_mprotect_rw_to_rd, "AT_All_PAGE_SIZES"); + +static void BM_syscall_mmap_file_mprotect_rw_to_none(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_READ | PROT_WRITE, + .to_prot = PROT_NONE, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapFile(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_mprotect_rw_to_none, "AT_All_PAGE_SIZES"); + +static void BM_syscall_mmap_file_mprotect_none_to_rw(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_NONE, + .to_prot = PROT_READ | PROT_WRITE, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapFile(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_mprotect_none_to_rw, "AT_All_PAGE_SIZES"); + +static void BM_syscall_mmap_file_mprotect_none_to_rd(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_NONE, + .to_prot = PROT_READ, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapFile(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_mprotect_none_to_rd, "AT_All_PAGE_SIZES"); + +static void BM_syscall_mmap_file_mprotect_rd_to_none(benchmark::State& state) { + struct MprotectParams params = { + .from_prot = PROT_READ, + .to_prot = PROT_NONE, + .size = state.range(0), + }; + MprotectBenchmarkWithMmapFile(state, params); +} +BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_mprotect_rd_to_none, "AT_All_PAGE_SIZES");