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