blob: fd2790da7f165b4072e48e14db43e146a0c6a6f9 [file] [log] [blame]
Eric Seckleredf3f7c2018-10-23 16:44:53 +01001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Primiano Tucci2c5488f2019-06-01 03:27:28 +010017#include "perfetto/ext/base/paged_memory.h"
Eric Seckleredf3f7c2018-10-23 16:44:53 +010018
19#include <stdint.h>
20
Eric Seckleredf3f7c2018-10-23 16:44:53 +010021#include "perfetto/base/build_config.h"
Primiano Tucci90d69302020-08-20 17:22:12 +020022#include "perfetto/ext/base/utils.h"
Eric Seckleredf3f7c2018-10-23 16:44:53 +010023#include "src/base/test/vm_test_utils.h"
Primiano Tucci919ca1e2019-08-21 20:26:58 +020024#include "test/gtest_and_gmock.h"
Eric Seckleredf3f7c2018-10-23 16:44:53 +010025
Primiano Tucci15f5e872020-07-27 23:08:05 +020026#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
27 !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
Eric Seckler201117a2018-12-18 13:13:39 +000028 !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
Eric Seckleredf3f7c2018-10-23 16:44:53 +010029#include <sys/resource.h>
30#endif
31
32namespace perfetto {
33namespace base {
34namespace {
35
36TEST(PagedMemoryTest, Basic) {
37 const size_t kNumPages = 10;
38 const size_t kSize = 4096 * kNumPages;
39 void* ptr_raw = nullptr;
40 {
41 PagedMemory mem = PagedMemory::Allocate(kSize);
42 ASSERT_TRUE(mem.IsValid());
43 ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(mem.Get()) % 4096);
44 ptr_raw = mem.Get();
45 for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
46 ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
47
Eric Seckler201117a2018-12-18 13:13:39 +000048#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
Eric Seckleredf3f7c2018-10-23 16:44:53 +010049 ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kSize));
Eric Seckler201117a2018-12-18 13:13:39 +000050#endif
Eric Seckleredf3f7c2018-10-23 16:44:53 +010051
52#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
53 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
54 ASSERT_TRUE(mem.AdviseDontNeed(ptr_raw, kSize));
55
56 // Make sure the pages were removed from the working set.
57 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
58#endif
59 }
60
Eric Seckler201117a2018-12-18 13:13:39 +000061#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
Eric Seckleredf3f7c2018-10-23 16:44:53 +010062 // Freed memory is necessarily not mapped in to the process.
63 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
Eric Seckler201117a2018-12-18 13:13:39 +000064#endif
Eric Seckleredf3f7c2018-10-23 16:44:53 +010065}
66
Primiano Tucci90d69302020-08-20 17:22:12 +020067TEST(PagedMemoryTest, SubPageGranularity) {
68 const size_t kSize = GetSysPageSize() + 1024;
69 PagedMemory mem = PagedMemory::Allocate(kSize);
70 ASSERT_TRUE(mem.IsValid());
71 ASSERT_EQ(0u, reinterpret_cast<uintptr_t>(mem.Get()) % GetSysPageSize());
72 void* ptr_raw = mem.Get();
73 for (size_t i = 0; i < kSize / sizeof(uint64_t); i++) {
74 auto* ptr64 = reinterpret_cast<volatile uint64_t*>(ptr_raw) + i;
75 ASSERT_EQ(0u, *ptr64);
76 *ptr64 = i;
77 }
78
79#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
80 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
81 // Do an AdviseDontNeed on the whole range, which is NOT an integer multiple
82 // of the page size. The initial page must be cleared. The remaining 1024
83 // might or might not be cleared depending on the OS implementation.
84 ASSERT_TRUE(mem.AdviseDontNeed(ptr_raw, kSize));
85 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, GetSysPageSize()));
86 for (size_t i = 0; i < GetSysPageSize() / sizeof(uint64_t); i++) {
87 auto* ptr64 = reinterpret_cast<volatile uint64_t*>(ptr_raw) + i;
88 ASSERT_EQ(0u, *ptr64);
89 }
90#endif
91}
92
Eric Seckleredf3f7c2018-10-23 16:44:53 +010093TEST(PagedMemoryTest, Uncommitted) {
Eric Seckler2ad5a7d2018-10-24 09:43:19 +010094 constexpr size_t kNumPages = 4096;
Eric Seckleredf3f7c2018-10-23 16:44:53 +010095 constexpr size_t kSize = 4096 * kNumPages;
96 char* ptr_raw = nullptr;
97 {
98 PagedMemory mem = PagedMemory::Allocate(kSize, PagedMemory::kDontCommit);
99 ASSERT_TRUE(mem.IsValid());
100 ptr_raw = reinterpret_cast<char*>(mem.Get());
101
102#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
Eric Seckler2ad5a7d2018-10-24 09:43:19 +0100103 // Windows only commits the first 1024 pages.
104 constexpr size_t kMappedSize = 4096 * 1024;
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100105
106 for (size_t i = 0; i < kMappedSize / sizeof(uint64_t); i++)
107 ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
108
109 ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kMappedSize));
110
111 // Next page shouldn't be mapped.
112 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw + kMappedSize, 4096));
Stephen Nuskoaca77482020-01-10 15:47:06 +0000113 EXPECT_DEATH_IF_SUPPORTED({ ptr_raw[kMappedSize] = 'x'; }, ".*");
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100114
115 // Commit the remaining pages.
116 mem.EnsureCommitted(kSize);
117
118 for (size_t i = kMappedSize / sizeof(uint64_t);
119 i < kSize / sizeof(uint64_t); i++) {
120 ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
121 }
Eric Seckler201117a2018-12-18 13:13:39 +0000122#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
123 // Fuchsia doesn't yet support paging. So this should be a no-op.
124 mem.EnsureCommitted(kSize);
125 for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
126 ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100127#else
128 // Linux only maps on access.
129 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
130
131 // This should not have any effect.
132 mem.EnsureCommitted(kSize);
133 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
134
135 for (size_t i = 0; i < kSize / sizeof(uint64_t); i++)
136 ASSERT_EQ(0u, *(reinterpret_cast<uint64_t*>(mem.Get()) + i));
137 ASSERT_TRUE(vm_test_utils::IsMapped(ptr_raw, kSize));
138#endif
139 }
140
Eric Seckler201117a2018-12-18 13:13:39 +0000141#if !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100142 // Freed memory is necessarily not mapped in to the process.
143 ASSERT_FALSE(vm_test_utils::IsMapped(ptr_raw, kSize));
Eric Seckler201117a2018-12-18 13:13:39 +0000144#endif
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100145}
146
Eric Seckler2ad5a7d2018-10-24 09:43:19 +0100147#if defined(ADDRESS_SANITIZER)
148TEST(PagedMemoryTest, AccessUncommittedMemoryTriggersASAN) {
Stephen Nuskoaca77482020-01-10 15:47:06 +0000149 EXPECT_DEATH_IF_SUPPORTED(
Eric Seckler2ad5a7d2018-10-24 09:43:19 +0100150 {
151 constexpr size_t kNumPages = 4096;
152 constexpr size_t kSize = 4096 * kNumPages;
153 PagedMemory mem =
154 PagedMemory::Allocate(kSize, PagedMemory::kDontCommit);
155 ASSERT_TRUE(mem.IsValid());
156 char* ptr_raw = reinterpret_cast<char*>(mem.Get());
157 // Only the first 1024 pages are mapped.
158 constexpr size_t kMappedSize = 4096 * 1024;
159 ptr_raw[kMappedSize] = 'x';
160 abort();
161 },
Eric Seckler7c0e0be2019-03-19 09:23:40 +0000162 "AddressSanitizer: .*");
Eric Seckler2ad5a7d2018-10-24 09:43:19 +0100163}
164#endif // ADDRESS_SANITIZER
165
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100166TEST(PagedMemoryTest, GuardRegions) {
Primiano Tucci7e2fecb2020-09-21 21:00:16 +0200167 const size_t kSize = GetSysPageSize();
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100168 PagedMemory mem = PagedMemory::Allocate(kSize);
169 ASSERT_TRUE(mem.IsValid());
170 volatile char* raw = reinterpret_cast<char*>(mem.Get());
Stephen Nuskoaca77482020-01-10 15:47:06 +0000171 EXPECT_DEATH_IF_SUPPORTED({ raw[-1] = 'x'; }, ".*");
172 EXPECT_DEATH_IF_SUPPORTED({ raw[kSize] = 'x'; }, ".*");
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100173}
174
175// Disable this on:
176// MacOS: because it doesn't seem to have an equivalent rlimit to bound mmap().
Eric Seckler201117a2018-12-18 13:13:39 +0000177// Fuchsia: doesn't support rlimit.
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100178// Sanitizers: they seem to try to shadow mmaped memory and fail due to OOMs.
Primiano Tucci15f5e872020-07-27 23:08:05 +0200179#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
Eric Seckler201117a2018-12-18 13:13:39 +0000180 !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
181 !PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) && !defined(ADDRESS_SANITIZER) && \
182 !defined(LEAK_SANITIZER) && !defined(THREAD_SANITIZER) && \
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100183 !defined(MEMORY_SANITIZER)
184// Glibc headers hit this on RLIMIT_ macros.
185#pragma GCC diagnostic push
186#if defined(__clang__)
187#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion"
188#endif
189TEST(PagedMemoryTest, Unchecked) {
190 const size_t kMemLimit = 256 * 1024 * 1024l;
191 struct rlimit limit {
192 kMemLimit, kMemLimit
193 };
194 // ASSERT_EXIT here is to spawn the test in a sub-process and avoid
195 // propagating the setrlimit() to other test units in case of failure.
196 ASSERT_EXIT(
197 {
198 ASSERT_EQ(0, setrlimit(RLIMIT_AS, &limit));
199 auto mem = PagedMemory::Allocate(kMemLimit * 2, PagedMemory::kMayFail);
200 ASSERT_FALSE(mem.IsValid());
Eric Seckler7cc91752019-02-13 09:57:10 +0000201 // Use _exit() instead of exit() to avoid calling destructors on child
202 // process death, which may interfere with the parent process's test
203 // launcher expectations.
204 _exit(0);
Eric Seckleredf3f7c2018-10-23 16:44:53 +0100205 },
206 ::testing::ExitedWithCode(0), "");
207}
208#pragma GCC diagnostic pop
209#endif
210
211} // namespace
212} // namespace base
213} // namespace perfetto