1 /*
2 * Copyright (C) 2020 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 <malloc.h>
18 #include <unistd.h>
19
20 #include "perfetto/base/logging.h"
21 #include "perfetto/heap_profile.h"
22 #include "src/profiling/memory/wrap_allocators.h"
23
24 namespace {
25 // AHeapProfile_registerHeap is guaranteed to be safe to call from global
26 // constructors.
27 #pragma GCC diagnostic push
28 #pragma GCC diagnostic ignored "-Wglobal-constructors"
29 uint32_t g_heap_id = AHeapProfile_registerHeap(AHeapInfo_create("libc.malloc"));
30 #pragma GCC diagnostic pop
31
IsPowerOfTwo(size_t v)32 bool IsPowerOfTwo(size_t v) {
33 return (v != 0 && ((v & (v - 1)) == 0));
34 }
35
36 // The code inside the perfetto::profiling::wrap_ functions has been designed to
37 // avoid calling malloc/free functions, but, in some rare cases, this happens
38 // anyway inside glibc. The code belows prevents this reentrancy with a thread
39 // local variable, because:
40 // * It can cause infinite recursion.
41 // * If any lock is needed inside glibc, it can cause a deadlock.
42
43 // True if this thread is already inside heapprofd wrappers.
44 thread_local bool inside_wrapper = false;
45
46 class ScopedReentrancyPreventer {
47 public:
48 // Precondition: is_inside is false.
ScopedReentrancyPreventer()49 ScopedReentrancyPreventer() { inside_wrapper = true; }
~ScopedReentrancyPreventer()50 ~ScopedReentrancyPreventer() { inside_wrapper = false; }
is_inside()51 static bool is_inside() { return inside_wrapper; }
52 };
53
54 } // namespace
55
56 extern "C" {
57
58 // These are exported by GLibc to be used by functions overwriting malloc
59 // to call back to the real implementation.
60 extern void* __libc_malloc(size_t);
61 extern void __libc_free(void*);
62 extern void* __libc_calloc(size_t, size_t);
63 extern void* __libc_realloc(void*, size_t);
64 extern void* __libc_memalign(size_t, size_t);
65 extern void* __libc_pvalloc(size_t);
66 extern void* __libc_valloc(size_t);
67 extern void* __libc_reallocarray(void*, size_t, size_t);
68
69 #pragma GCC visibility push(default)
70
malloc(size_t size)71 void* malloc(size_t size) {
72 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
73 return __libc_malloc(size);
74 }
75 ScopedReentrancyPreventer p;
76
77 return perfetto::profiling::wrap_malloc(g_heap_id, __libc_malloc, size);
78 }
79
free(void * ptr)80 void free(void* ptr) {
81 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
82 return __libc_free(ptr);
83 }
84 ScopedReentrancyPreventer p;
85
86 return perfetto::profiling::wrap_free(g_heap_id, __libc_free, ptr);
87 }
88
calloc(size_t nmemb,size_t size)89 void* calloc(size_t nmemb, size_t size) {
90 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
91 return __libc_calloc(nmemb, size);
92 }
93 ScopedReentrancyPreventer p;
94
95 return perfetto::profiling::wrap_calloc(g_heap_id, __libc_calloc, nmemb,
96 size);
97 }
98
realloc(void * ptr,size_t size)99 void* realloc(void* ptr, size_t size) {
100 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
101 return __libc_realloc(ptr, size);
102 }
103 ScopedReentrancyPreventer p;
104
105 return perfetto::profiling::wrap_realloc(g_heap_id, __libc_realloc, ptr,
106 size);
107 }
108
posix_memalign(void ** memptr,size_t alignment,size_t size)109 int posix_memalign(void** memptr, size_t alignment, size_t size) {
110 if (alignment % sizeof(void*) || !IsPowerOfTwo(alignment / sizeof(void*))) {
111 return EINVAL;
112 }
113
114 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
115 void* alloc = __libc_memalign(alignment, size);
116 if (!alloc) {
117 return ENOMEM;
118 }
119 *memptr = alloc;
120 return 0;
121 }
122 ScopedReentrancyPreventer p;
123
124 void* alloc = perfetto::profiling::wrap_memalign(g_heap_id, __libc_memalign,
125 alignment, size);
126 if (!alloc) {
127 return ENOMEM;
128 }
129 *memptr = alloc;
130 return 0;
131 }
132
aligned_alloc(size_t alignment,size_t size)133 void* aligned_alloc(size_t alignment, size_t size) {
134 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
135 return __libc_memalign(alignment, size);
136 }
137 ScopedReentrancyPreventer p;
138
139 return perfetto::profiling::wrap_memalign(g_heap_id, __libc_memalign,
140 alignment, size);
141 }
142
memalign(size_t alignment,size_t size)143 void* memalign(size_t alignment, size_t size) {
144 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
145 return __libc_memalign(alignment, size);
146 }
147 ScopedReentrancyPreventer p;
148
149 return perfetto::profiling::wrap_memalign(g_heap_id, __libc_memalign,
150 alignment, size);
151 }
152
pvalloc(size_t size)153 void* pvalloc(size_t size) {
154 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
155 return __libc_pvalloc(size);
156 }
157 ScopedReentrancyPreventer p;
158
159 return perfetto::profiling::wrap_pvalloc(g_heap_id, __libc_pvalloc, size);
160 }
161
valloc(size_t size)162 void* valloc(size_t size) {
163 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
164 return __libc_valloc(size);
165 }
166 ScopedReentrancyPreventer p;
167
168 return perfetto::profiling::wrap_valloc(g_heap_id, __libc_valloc, size);
169 }
170
reallocarray(void * ptr,size_t nmemb,size_t size)171 void* reallocarray(void* ptr, size_t nmemb, size_t size) {
172 if (PERFETTO_UNLIKELY(ScopedReentrancyPreventer::is_inside())) {
173 return __libc_reallocarray(ptr, nmemb, size);
174 }
175 ScopedReentrancyPreventer p;
176
177 return perfetto::profiling::wrap_reallocarray(g_heap_id, __libc_reallocarray,
178 ptr, nmemb, size);
179 }
180
181 #pragma GCC visibility pop
182 }
183