• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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