• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 #include "perfetto/ext/base/paged_memory.h"
18 
19 #include <algorithm>
20 #include <cmath>
21 #include <cstddef>
22 
23 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
24 #include <Windows.h>
25 #else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
26 #include <sys/mman.h>
27 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
28 
29 #include "perfetto/base/logging.h"
30 #include "perfetto/ext/base/container_annotations.h"
31 #include "perfetto/ext/base/utils.h"
32 
33 namespace perfetto {
34 namespace base {
35 
36 namespace {
37 
38 #if TRACK_COMMITTED_SIZE()
39 constexpr size_t kCommitChunkSize = 4 * 1024 * 1024;  // 4MB
40 #endif
41 
RoundUpToSysPageSize(size_t req_size)42 size_t RoundUpToSysPageSize(size_t req_size) {
43   const size_t page_size = GetSysPageSize();
44   return (req_size + page_size - 1) & ~(page_size - 1);
45 }
46 
GuardSize()47 size_t GuardSize() {
48   return GetSysPageSize();
49 }
50 
51 }  // namespace
52 
53 // static
Allocate(size_t req_size,int flags)54 PagedMemory PagedMemory::Allocate(size_t req_size, int flags) {
55   size_t rounded_up_size = RoundUpToSysPageSize(req_size);
56   PERFETTO_CHECK(rounded_up_size >= req_size);
57   size_t outer_size = rounded_up_size + GuardSize() * 2;
58 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
59   void* ptr = VirtualAlloc(nullptr, outer_size, MEM_RESERVE, PAGE_NOACCESS);
60   if (!ptr && (flags & kMayFail))
61     return PagedMemory();
62   PERFETTO_CHECK(ptr);
63   char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
64 #else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
65   void* ptr = mmap(nullptr, outer_size, PROT_READ | PROT_WRITE,
66                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
67   if (ptr == MAP_FAILED && (flags & kMayFail))
68     return PagedMemory();
69   PERFETTO_CHECK(ptr && ptr != MAP_FAILED);
70   char* usable_region = reinterpret_cast<char*>(ptr) + GuardSize();
71   int res = mprotect(ptr, GuardSize(), PROT_NONE);
72   res |= mprotect(usable_region + rounded_up_size, GuardSize(), PROT_NONE);
73   PERFETTO_CHECK(res == 0);
74 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
75 
76   auto memory = PagedMemory(usable_region, req_size);
77 #if TRACK_COMMITTED_SIZE()
78   size_t initial_commit = req_size;
79   if (flags & kDontCommit)
80     initial_commit = std::min(initial_commit, kCommitChunkSize);
81   memory.EnsureCommitted(initial_commit);
82 #endif  // TRACK_COMMITTED_SIZE()
83   return memory;
84 }
85 
PagedMemory()86 PagedMemory::PagedMemory() {}
87 
88 // clang-format off
PagedMemory(char * p,size_t size)89 PagedMemory::PagedMemory(char* p, size_t size) : p_(p), size_(size) {
90   ANNOTATE_NEW_BUFFER(p_, size_, committed_size_)
91 }
92 
PagedMemory(PagedMemory && other)93 PagedMemory::PagedMemory(PagedMemory&& other) noexcept {
94   *this = other;
95   other.p_ = nullptr;
96 }
97 // clang-format on
98 
operator =(PagedMemory && other)99 PagedMemory& PagedMemory::operator=(PagedMemory&& other) {
100   this->~PagedMemory();
101   new (this) PagedMemory(std::move(other));
102   return *this;
103 }
104 
~PagedMemory()105 PagedMemory::~PagedMemory() {
106   if (!p_)
107     return;
108   PERFETTO_CHECK(size_);
109   char* start = p_ - GuardSize();
110 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
111   BOOL res = VirtualFree(start, 0, MEM_RELEASE);
112   PERFETTO_CHECK(res != 0);
113 #else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
114   const size_t outer_size = RoundUpToSysPageSize(size_) + GuardSize() * 2;
115   int res = munmap(start, outer_size);
116   PERFETTO_CHECK(res == 0);
117 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
118   ANNOTATE_DELETE_BUFFER(p_, size_, committed_size_)
119 }
120 
AdviseDontNeed(void * p,size_t size)121 bool PagedMemory::AdviseDontNeed(void* p, size_t size) {
122   PERFETTO_DCHECK(p_);
123   PERFETTO_DCHECK(p >= p_);
124   PERFETTO_DCHECK(static_cast<char*>(p) + size <= p_ + size_);
125 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
126   // Discarding pages on Windows has more CPU cost than is justified for the
127   // possible memory savings.
128   return false;
129 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_QNX)
130   int res = posix_madvise(p, size, POSIX_MADV_DISCARD_NP);
131   PERFETTO_DCHECK(res == 0);
132   return true;
133 #else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
134         // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
135   // http://man7.org/linux/man-pages/man2/madvise.2.html
136   int res = madvise(p, size, MADV_DONTNEED);
137   PERFETTO_DCHECK(res == 0);
138   return true;
139 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) ||
140         // PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
141 }
142 
143 #if TRACK_COMMITTED_SIZE()
EnsureCommitted(size_t committed_size)144 void PagedMemory::EnsureCommitted(size_t committed_size) {
145   PERFETTO_DCHECK(committed_size <= size_);
146 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
147   if (committed_size_ >= committed_size)
148     return;
149   // Rounding up.
150   size_t delta = committed_size - committed_size_;
151   size_t num_additional_chunks =
152       (delta + kCommitChunkSize - 1) / kCommitChunkSize;
153   PERFETTO_DCHECK(num_additional_chunks * kCommitChunkSize >= delta);
154   // Don't commit more than the total size.
155   size_t commit_size = std::min(num_additional_chunks * kCommitChunkSize,
156                                 size_ - committed_size_);
157   void* res = VirtualAlloc(p_ + committed_size_, commit_size, MEM_COMMIT,
158                            PAGE_READWRITE);
159   PERFETTO_CHECK(res);
160   ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_,
161                        committed_size_ + commit_size)
162   committed_size_ += commit_size;
163 #else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
164   // mmap commits automatically as needed, so we only track here for ASAN.
165   committed_size = std::max(committed_size_, committed_size);
166   ANNOTATE_CHANGE_SIZE(p_, size_, committed_size_, committed_size)
167   committed_size_ = committed_size;
168 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
169 }
170 #endif  // TRACK_COMMITTED_SIZE()
171 
172 }  // namespace base
173 }  // namespace perfetto
174