Fix: lttng-ust needs -ldl for tracef()
[lttng-ust.git] / liblttng-ust-libc-wrapper / lttng-ust-malloc.c
CommitLineData
b27f8e75
MD
1/*
2 * Copyright (C) 2009 Pierre-Marc Fournier
1622ba22 3 * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
c39c72ee
PMF
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
e541a28d 20#define _GNU_SOURCE
f02baefb 21#include <lttng/ust-dlfcn.h>
e541a28d
PMF
22#include <sys/types.h>
23#include <stdio.h>
2594a5b4 24#include <assert.h>
4c3536e0
MD
25#include <urcu/system.h>
26#include <urcu/uatomic.h>
2594a5b4 27#include <urcu/compiler.h>
8c06ba6f 28#include <urcu/tls-compat.h>
2594a5b4 29#include <lttng/align.h>
1622ba22
MD
30
31#define TRACEPOINT_DEFINE
32#define TRACEPOINT_CREATE_PROBES
33#include "ust_libc.h"
fbd8191b 34
f95b2888
SS
35#define STATIC_CALLOC_LEN 4096
36static char static_calloc_buf[STATIC_CALLOC_LEN];
4c3536e0 37static unsigned long static_calloc_buf_offset;
f95b2888 38
2594a5b4
MD
39struct alloc_functions {
40 void *(*calloc)(size_t nmemb, size_t size);
41 void *(*malloc)(size_t size);
42 void (*free)(void *ptr);
43 void *(*realloc)(void *ptr, size_t size);
44 void *(*memalign)(size_t alignment, size_t size);
45 int (*posix_memalign)(void **memptr, size_t alignment, size_t size);
46};
47
48static
49struct alloc_functions cur_alloc;
50
8c06ba6f
MD
51/*
52 * Make sure our own use of the LTS compat layer will not cause infinite
53 * recursion by calling calloc.
54 */
55
56static
57void *static_calloc(size_t nmemb, size_t size);
58
59#define calloc static_calloc
60static DEFINE_URCU_TLS(int, malloc_nesting);
61#undef calloc
62
2594a5b4
MD
63/*
64 * Static allocator to use when initially executing dlsym(). It keeps a
65 * size_t value of each object size prior to the object.
66 */
67static
68void *static_calloc_aligned(size_t nmemb, size_t size, size_t alignment)
f95b2888 69{
2594a5b4
MD
70 size_t prev_offset, new_offset, res_offset, aligned_offset;
71
72 if (nmemb * size == 0) {
73 return NULL;
74 }
f95b2888 75
4c3536e0
MD
76 /*
77 * Protect static_calloc_buf_offset from concurrent updates
78 * using a cmpxchg loop rather than a mutex to remove a
79 * dependency on pthread. This will minimize the risk of bad
80 * interaction between mutex and malloc instrumentation.
81 */
82 res_offset = CMM_LOAD_SHARED(static_calloc_buf_offset);
83 do {
84 prev_offset = res_offset;
2594a5b4
MD
85 aligned_offset = ALIGN(prev_offset + sizeof(size_t), alignment);
86 new_offset = aligned_offset + nmemb * size;
87 if (new_offset > sizeof(static_calloc_buf)) {
88 abort();
4c3536e0 89 }
4c3536e0
MD
90 } while ((res_offset = uatomic_cmpxchg(&static_calloc_buf_offset,
91 prev_offset, new_offset)) != prev_offset);
2594a5b4
MD
92 *(size_t *) &static_calloc_buf[aligned_offset - sizeof(size_t)] = size;
93 return &static_calloc_buf[aligned_offset];
94}
95
96static
97void *static_calloc(size_t nmemb, size_t size)
98{
99 void *retval;
100
101 retval = static_calloc_aligned(nmemb, size, 1);
2594a5b4
MD
102 return retval;
103}
104
105static
106void *static_malloc(size_t size)
107{
108 void *retval;
109
110 retval = static_calloc_aligned(1, size, 1);
2594a5b4
MD
111 return retval;
112}
113
114static
115void static_free(void *ptr)
116{
117 /* no-op. */
2594a5b4
MD
118}
119
120static
121void *static_realloc(void *ptr, size_t size)
122{
123 size_t *old_size = NULL;
124 void *retval;
125
126 if (size == 0) {
127 retval = NULL;
128 goto end;
129 }
130
131 if (ptr) {
132 old_size = (size_t *) ptr - 1;
133 if (size <= *old_size) {
134 /* We can re-use the old entry. */
135 *old_size = size;
136 retval = ptr;
137 goto end;
138 }
139 }
140 /* We need to expand. Don't free previous memory location. */
141 retval = static_calloc_aligned(1, size, 1);
142 assert(retval);
143 if (ptr)
144 memcpy(retval, ptr, *old_size);
145end:
2594a5b4
MD
146 return retval;
147}
148
149static
150void *static_memalign(size_t alignment, size_t size)
151{
152 void *retval;
153
154 retval = static_calloc_aligned(1, size, alignment);
2594a5b4
MD
155 return retval;
156}
157
158static
159int static_posix_memalign(void **memptr, size_t alignment, size_t size)
160{
2594a5b4
MD
161 void *ptr;
162
163 /* Check for power of 2, larger than void *. */
164 if (alignment & (alignment - 1)
165 || alignment < sizeof(void *)
166 || alignment == 0) {
2594a5b4
MD
167 goto end;
168 }
169 ptr = static_calloc_aligned(1, size, alignment);
170 *memptr = ptr;
2594a5b4 171end:
2594a5b4
MD
172 return 0;
173}
174
175static
176void setup_static_allocator(void)
177{
178 assert(cur_alloc.calloc == NULL);
179 cur_alloc.calloc = static_calloc;
180 assert(cur_alloc.malloc == NULL);
181 cur_alloc.malloc = static_malloc;
182 assert(cur_alloc.free == NULL);
183 cur_alloc.free = static_free;
184 assert(cur_alloc.realloc == NULL);
185 cur_alloc.realloc = static_realloc;
186 assert(cur_alloc.memalign == NULL);
187 cur_alloc.memalign = static_memalign;
188 assert(cur_alloc.posix_memalign == NULL);
189 cur_alloc.posix_memalign = static_posix_memalign;
190}
191
192static
193void lookup_all_symbols(void)
194{
195 struct alloc_functions af;
196
197 /*
198 * Temporarily redirect allocation functions to
199 * static_calloc_aligned, and free function to static_free
200 * (no-op), until the dlsym lookup has completed.
201 */
202 setup_static_allocator();
203
204 /* Perform the actual lookups */
205 af.calloc = dlsym(RTLD_NEXT, "calloc");
206 af.malloc = dlsym(RTLD_NEXT, "malloc");
207 af.free = dlsym(RTLD_NEXT, "free");
208 af.realloc = dlsym(RTLD_NEXT, "realloc");
209 af.memalign = dlsym(RTLD_NEXT, "memalign");
210 af.posix_memalign = dlsym(RTLD_NEXT, "posix_memalign");
211
212 /* Populate the new allocator functions */
213 memcpy(&cur_alloc, &af, sizeof(cur_alloc));
f95b2888
SS
214}
215
e541a28d
PMF
216void *malloc(size_t size)
217{
1c184644
PMF
218 void *retval;
219
8c06ba6f 220 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
221 if (cur_alloc.malloc == NULL) {
222 lookup_all_symbols();
223 if (cur_alloc.malloc == NULL) {
e541a28d 224 fprintf(stderr, "mallocwrap: unable to find malloc\n");
2594a5b4 225 abort();
e541a28d
PMF
226 }
227 }
2594a5b4 228 retval = cur_alloc.malloc(size);
8c06ba6f
MD
229 if (URCU_TLS(malloc_nesting) == 1) {
230 tracepoint(ust_libc, malloc, size, retval);
231 }
232 URCU_TLS(malloc_nesting)--;
1c184644
PMF
233 return retval;
234}
235
236void free(void *ptr)
237{
8c06ba6f 238 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
239 /*
240 * Check whether the memory was allocated with
241 * static_calloc_align, in which case there is nothing to free.
f95b2888 242 */
2594a5b4
MD
243 if (caa_unlikely((char *)ptr >= static_calloc_buf &&
244 (char *)ptr < static_calloc_buf + STATIC_CALLOC_LEN)) {
8c06ba6f
MD
245 goto end;
246 }
247
248 if (URCU_TLS(malloc_nesting) == 1) {
249 tracepoint(ust_libc, free, ptr);
f95b2888 250 }
1c184644 251
2594a5b4
MD
252 if (cur_alloc.free == NULL) {
253 lookup_all_symbols();
254 if (cur_alloc.free == NULL) {
1c184644 255 fprintf(stderr, "mallocwrap: unable to find free\n");
2594a5b4 256 abort();
1c184644
PMF
257 }
258 }
2594a5b4 259 cur_alloc.free(ptr);
8c06ba6f
MD
260end:
261 URCU_TLS(malloc_nesting)--;
e541a28d 262}
f95b2888
SS
263
264void *calloc(size_t nmemb, size_t size)
265{
f95b2888
SS
266 void *retval;
267
8c06ba6f 268 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
269 if (cur_alloc.calloc == NULL) {
270 lookup_all_symbols();
271 if (cur_alloc.calloc == NULL) {
f95b2888 272 fprintf(stderr, "callocwrap: unable to find calloc\n");
2594a5b4 273 abort();
f95b2888
SS
274 }
275 }
2594a5b4 276 retval = cur_alloc.calloc(nmemb, size);
8c06ba6f
MD
277 if (URCU_TLS(malloc_nesting) == 1) {
278 tracepoint(ust_libc, calloc, nmemb, size, retval);
279 }
280 URCU_TLS(malloc_nesting)--;
f95b2888
SS
281 return retval;
282}
283
284void *realloc(void *ptr, size_t size)
285{
f95b2888
SS
286 void *retval;
287
8c06ba6f
MD
288 URCU_TLS(malloc_nesting)++;
289 /*
290 * Check whether the memory was allocated with
2594a5b4
MD
291 * static_calloc_align, in which case there is nothing
292 * to free, and we need to copy the old data.
293 */
294 if (caa_unlikely((char *)ptr >= static_calloc_buf &&
295 (char *)ptr < static_calloc_buf + STATIC_CALLOC_LEN)) {
296 size_t *old_size;
297
298 old_size = (size_t *) ptr - 1;
299 if (cur_alloc.calloc == NULL) {
300 lookup_all_symbols();
301 if (cur_alloc.calloc == NULL) {
302 fprintf(stderr, "reallocwrap: unable to find calloc\n");
303 abort();
304 }
305 }
306 retval = cur_alloc.calloc(1, size);
307 if (retval) {
308 memcpy(retval, ptr, *old_size);
309 }
8c06ba6f
MD
310 /*
311 * Mimick that a NULL pointer has been received, so
312 * memory allocation analysis based on the trace don't
313 * get confused by the address from the static
314 * allocator.
315 */
316 ptr = NULL;
2594a5b4
MD
317 goto end;
318 }
319
320 if (cur_alloc.realloc == NULL) {
321 lookup_all_symbols();
322 if (cur_alloc.realloc == NULL) {
f95b2888 323 fprintf(stderr, "reallocwrap: unable to find realloc\n");
2594a5b4 324 abort();
f95b2888
SS
325 }
326 }
2594a5b4
MD
327 retval = cur_alloc.realloc(ptr, size);
328end:
8c06ba6f
MD
329 if (URCU_TLS(malloc_nesting) == 1) {
330 tracepoint(ust_libc, realloc, ptr, size, retval);
331 }
332 URCU_TLS(malloc_nesting)--;
f95b2888
SS
333 return retval;
334}
9d34b226
SS
335
336void *memalign(size_t alignment, size_t size)
337{
9d34b226
SS
338 void *retval;
339
8c06ba6f 340 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
341 if (cur_alloc.memalign == NULL) {
342 lookup_all_symbols();
343 if (cur_alloc.memalign == NULL) {
9d34b226 344 fprintf(stderr, "memalignwrap: unable to find memalign\n");
2594a5b4 345 abort();
9d34b226
SS
346 }
347 }
2594a5b4 348 retval = cur_alloc.memalign(alignment, size);
8c06ba6f
MD
349 if (URCU_TLS(malloc_nesting) == 1) {
350 tracepoint(ust_libc, memalign, alignment, size, retval);
351 }
352 URCU_TLS(malloc_nesting)--;
9d34b226
SS
353 return retval;
354}
355
356int posix_memalign(void **memptr, size_t alignment, size_t size)
357{
9d34b226
SS
358 int retval;
359
8c06ba6f 360 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
361 if (cur_alloc.posix_memalign == NULL) {
362 lookup_all_symbols();
363 if (cur_alloc.posix_memalign == NULL) {
9d34b226 364 fprintf(stderr, "posix_memalignwrap: unable to find posix_memalign\n");
2594a5b4 365 abort();
9d34b226
SS
366 }
367 }
2594a5b4 368 retval = cur_alloc.posix_memalign(memptr, alignment, size);
8c06ba6f
MD
369 if (URCU_TLS(malloc_nesting) == 1) {
370 tracepoint(ust_libc, posix_memalign, *memptr, alignment, size,
371 retval);
372 }
373 URCU_TLS(malloc_nesting)--;
9d34b226
SS
374 return retval;
375}
2594a5b4
MD
376
377__attribute__((constructor))
378void lttng_ust_malloc_wrapper_init(void)
379{
380 /* Initialization already done */
381 if (cur_alloc.calloc) {
382 return;
383 }
384 /*
385 * Ensure the allocator is in place before the process becomes
386 * multithreaded.
387 */
388 lookup_all_symbols();
389}
This page took 0.046136 seconds and 4 git commands to generate.