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