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");