Fix: all lttng-ust source files should be tagged _LGPL_SOURCE
[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
6d4658aa 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c39c72ee
PMF
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
1ddceb36 21#define _LGPL_SOURCE
f02baefb 22#include <lttng/ust-dlfcn.h>
e541a28d
PMF
23#include <sys/types.h>
24#include <stdio.h>
2594a5b4 25#include <assert.h>
4c3536e0
MD
26#include <urcu/system.h>
27#include <urcu/uatomic.h>
2594a5b4 28#include <urcu/compiler.h>
8c06ba6f 29#include <urcu/tls-compat.h>
20ef5166 30#include <urcu/arch.h>
2594a5b4 31#include <lttng/align.h>
171fcc6f 32#include <helper.h>
1622ba22
MD
33
34#define TRACEPOINT_DEFINE
35#define TRACEPOINT_CREATE_PROBES
52c95399 36#define TP_IP_PARAM ip
1622ba22 37#include "ust_libc.h"
fbd8191b 38
f95b2888
SS
39#define STATIC_CALLOC_LEN 4096
40static char static_calloc_buf[STATIC_CALLOC_LEN];
4c3536e0 41static unsigned long static_calloc_buf_offset;
f95b2888 42
2594a5b4
MD
43struct alloc_functions {
44 void *(*calloc)(size_t nmemb, size_t size);
45 void *(*malloc)(size_t size);
46 void (*free)(void *ptr);
47 void *(*realloc)(void *ptr, size_t size);
48 void *(*memalign)(size_t alignment, size_t size);
49 int (*posix_memalign)(void **memptr, size_t alignment, size_t size);
50};
51
52static
53struct alloc_functions cur_alloc;
54
8c06ba6f
MD
55/*
56 * Make sure our own use of the LTS compat layer will not cause infinite
57 * recursion by calling calloc.
58 */
59
60static
61void *static_calloc(size_t nmemb, size_t size);
62
20ef5166
MD
63/*
64 * pthread mutex replacement for URCU tls compat layer.
65 */
66static int ust_malloc_lock;
67
68static __attribute__((unused))
69void ust_malloc_spin_lock(pthread_mutex_t *lock)
70{
71 /*
72 * The memory barrier within cmpxchg takes care of ordering
73 * memory accesses with respect to the start of the critical
74 * section.
75 */
76 while (uatomic_cmpxchg(&ust_malloc_lock, 0, 1) != 0)
77 caa_cpu_relax();
78}
79
80static __attribute__((unused))
81void ust_malloc_spin_unlock(pthread_mutex_t *lock)
82{
83 /*
84 * Ensure memory accesses within the critical section do not
85 * leak outside.
86 */
87 cmm_smp_mb();
88 uatomic_set(&ust_malloc_lock, 0);
89}
90
8c06ba6f 91#define calloc static_calloc
20ef5166
MD
92#define pthread_mutex_lock ust_malloc_spin_lock
93#define pthread_mutex_unlock ust_malloc_spin_unlock
8c06ba6f 94static DEFINE_URCU_TLS(int, malloc_nesting);
20ef5166
MD
95#undef ust_malloc_spin_unlock
96#undef ust_malloc_spin_lock
8c06ba6f
MD
97#undef calloc
98
2594a5b4
MD
99/*
100 * Static allocator to use when initially executing dlsym(). It keeps a
101 * size_t value of each object size prior to the object.
102 */
103static
104void *static_calloc_aligned(size_t nmemb, size_t size, size_t alignment)
f95b2888 105{
2594a5b4
MD
106 size_t prev_offset, new_offset, res_offset, aligned_offset;
107
108 if (nmemb * size == 0) {
109 return NULL;
110 }
f95b2888 111
4c3536e0
MD
112 /*
113 * Protect static_calloc_buf_offset from concurrent updates
114 * using a cmpxchg loop rather than a mutex to remove a
115 * dependency on pthread. This will minimize the risk of bad
116 * interaction between mutex and malloc instrumentation.
117 */
118 res_offset = CMM_LOAD_SHARED(static_calloc_buf_offset);
119 do {
120 prev_offset = res_offset;
2594a5b4
MD
121 aligned_offset = ALIGN(prev_offset + sizeof(size_t), alignment);
122 new_offset = aligned_offset + nmemb * size;
123 if (new_offset > sizeof(static_calloc_buf)) {
124 abort();
4c3536e0 125 }
4c3536e0
MD
126 } while ((res_offset = uatomic_cmpxchg(&static_calloc_buf_offset,
127 prev_offset, new_offset)) != prev_offset);
2594a5b4
MD
128 *(size_t *) &static_calloc_buf[aligned_offset - sizeof(size_t)] = size;
129 return &static_calloc_buf[aligned_offset];
130}
131
132static
133void *static_calloc(size_t nmemb, size_t size)
134{
135 void *retval;
136
137 retval = static_calloc_aligned(nmemb, size, 1);
2594a5b4
MD
138 return retval;
139}
140
141static
142void *static_malloc(size_t size)
143{
144 void *retval;
145
146 retval = static_calloc_aligned(1, size, 1);
2594a5b4
MD
147 return retval;
148}
149
150static
151void static_free(void *ptr)
152{
153 /* no-op. */
2594a5b4
MD
154}
155
156static
157void *static_realloc(void *ptr, size_t size)
158{
159 size_t *old_size = NULL;
160 void *retval;
161
162 if (size == 0) {
163 retval = NULL;
164 goto end;
165 }
166
167 if (ptr) {
168 old_size = (size_t *) ptr - 1;
169 if (size <= *old_size) {
170 /* We can re-use the old entry. */
171 *old_size = size;
172 retval = ptr;
173 goto end;
174 }
175 }
176 /* We need to expand. Don't free previous memory location. */
177 retval = static_calloc_aligned(1, size, 1);
178 assert(retval);
179 if (ptr)
180 memcpy(retval, ptr, *old_size);
181end:
2594a5b4
MD
182 return retval;
183}
184
185static
186void *static_memalign(size_t alignment, size_t size)
187{
188 void *retval;
189
190 retval = static_calloc_aligned(1, size, alignment);
2594a5b4
MD
191 return retval;
192}
193
194static
195int static_posix_memalign(void **memptr, size_t alignment, size_t size)
196{
2594a5b4
MD
197 void *ptr;
198
199 /* Check for power of 2, larger than void *. */
200 if (alignment & (alignment - 1)
201 || alignment < sizeof(void *)
202 || alignment == 0) {
2594a5b4
MD
203 goto end;
204 }
205 ptr = static_calloc_aligned(1, size, alignment);
206 *memptr = ptr;
2594a5b4 207end:
2594a5b4
MD
208 return 0;
209}
210
211static
212void setup_static_allocator(void)
213{
214 assert(cur_alloc.calloc == NULL);
215 cur_alloc.calloc = static_calloc;
216 assert(cur_alloc.malloc == NULL);
217 cur_alloc.malloc = static_malloc;
218 assert(cur_alloc.free == NULL);
219 cur_alloc.free = static_free;
220 assert(cur_alloc.realloc == NULL);
221 cur_alloc.realloc = static_realloc;
222 assert(cur_alloc.memalign == NULL);
223 cur_alloc.memalign = static_memalign;
224 assert(cur_alloc.posix_memalign == NULL);
225 cur_alloc.posix_memalign = static_posix_memalign;
226}
227
228static
229void lookup_all_symbols(void)
230{
231 struct alloc_functions af;
232
233 /*
234 * Temporarily redirect allocation functions to
235 * static_calloc_aligned, and free function to static_free
236 * (no-op), until the dlsym lookup has completed.
237 */
238 setup_static_allocator();
239
240 /* Perform the actual lookups */
241 af.calloc = dlsym(RTLD_NEXT, "calloc");
242 af.malloc = dlsym(RTLD_NEXT, "malloc");
243 af.free = dlsym(RTLD_NEXT, "free");
244 af.realloc = dlsym(RTLD_NEXT, "realloc");
245 af.memalign = dlsym(RTLD_NEXT, "memalign");
246 af.posix_memalign = dlsym(RTLD_NEXT, "posix_memalign");
247
248 /* Populate the new allocator functions */
249 memcpy(&cur_alloc, &af, sizeof(cur_alloc));
f95b2888
SS
250}
251
e541a28d
PMF
252void *malloc(size_t size)
253{
1c184644
PMF
254 void *retval;
255
8c06ba6f 256 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
257 if (cur_alloc.malloc == NULL) {
258 lookup_all_symbols();
259 if (cur_alloc.malloc == NULL) {
e541a28d 260 fprintf(stderr, "mallocwrap: unable to find malloc\n");
2594a5b4 261 abort();
e541a28d
PMF
262 }
263 }
2594a5b4 264 retval = cur_alloc.malloc(size);
8c06ba6f 265 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 266 tracepoint(lttng_ust_libc, malloc,
171fcc6f 267 size, retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
268 }
269 URCU_TLS(malloc_nesting)--;
1c184644
PMF
270 return retval;
271}
272
273void free(void *ptr)
274{
8c06ba6f 275 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
276 /*
277 * Check whether the memory was allocated with
278 * static_calloc_align, in which case there is nothing to free.
f95b2888 279 */
2594a5b4
MD
280 if (caa_unlikely((char *)ptr >= static_calloc_buf &&
281 (char *)ptr < static_calloc_buf + STATIC_CALLOC_LEN)) {
8c06ba6f
MD
282 goto end;
283 }
284
285 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 286 tracepoint(lttng_ust_libc, free,
171fcc6f 287 ptr, LTTNG_UST_CALLER_IP());
f95b2888 288 }
1c184644 289
2594a5b4
MD
290 if (cur_alloc.free == NULL) {
291 lookup_all_symbols();
292 if (cur_alloc.free == NULL) {
1c184644 293 fprintf(stderr, "mallocwrap: unable to find free\n");
2594a5b4 294 abort();
1c184644
PMF
295 }
296 }
2594a5b4 297 cur_alloc.free(ptr);
8c06ba6f
MD
298end:
299 URCU_TLS(malloc_nesting)--;
e541a28d 300}
f95b2888
SS
301
302void *calloc(size_t nmemb, size_t size)
303{
f95b2888
SS
304 void *retval;
305
8c06ba6f 306 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
307 if (cur_alloc.calloc == NULL) {
308 lookup_all_symbols();
309 if (cur_alloc.calloc == NULL) {
f95b2888 310 fprintf(stderr, "callocwrap: unable to find calloc\n");
2594a5b4 311 abort();
f95b2888
SS
312 }
313 }
2594a5b4 314 retval = cur_alloc.calloc(nmemb, size);
8c06ba6f 315 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 316 tracepoint(lttng_ust_libc, calloc,
171fcc6f 317 nmemb, size, retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
318 }
319 URCU_TLS(malloc_nesting)--;
f95b2888
SS
320 return retval;
321}
322
323void *realloc(void *ptr, size_t size)
324{
f95b2888
SS
325 void *retval;
326
8c06ba6f
MD
327 URCU_TLS(malloc_nesting)++;
328 /*
329 * Check whether the memory was allocated with
2594a5b4
MD
330 * static_calloc_align, in which case there is nothing
331 * to free, and we need to copy the old data.
332 */
333 if (caa_unlikely((char *)ptr >= static_calloc_buf &&
334 (char *)ptr < static_calloc_buf + STATIC_CALLOC_LEN)) {
335 size_t *old_size;
336
337 old_size = (size_t *) ptr - 1;
338 if (cur_alloc.calloc == NULL) {
339 lookup_all_symbols();
340 if (cur_alloc.calloc == NULL) {
341 fprintf(stderr, "reallocwrap: unable to find calloc\n");
342 abort();
343 }
344 }
345 retval = cur_alloc.calloc(1, size);
346 if (retval) {
347 memcpy(retval, ptr, *old_size);
348 }
8c06ba6f
MD
349 /*
350 * Mimick that a NULL pointer has been received, so
351 * memory allocation analysis based on the trace don't
352 * get confused by the address from the static
353 * allocator.
354 */
355 ptr = NULL;
2594a5b4
MD
356 goto end;
357 }
358
359 if (cur_alloc.realloc == NULL) {
360 lookup_all_symbols();
361 if (cur_alloc.realloc == NULL) {
f95b2888 362 fprintf(stderr, "reallocwrap: unable to find realloc\n");
2594a5b4 363 abort();
f95b2888
SS
364 }
365 }
2594a5b4
MD
366 retval = cur_alloc.realloc(ptr, size);
367end:
8c06ba6f 368 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa 369 tracepoint(lttng_ust_libc, realloc,
171fcc6f 370 ptr, size, retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
371 }
372 URCU_TLS(malloc_nesting)--;
f95b2888
SS
373 return retval;
374}
9d34b226
SS
375
376void *memalign(size_t alignment, size_t size)
377{
9d34b226
SS
378 void *retval;
379
8c06ba6f 380 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
381 if (cur_alloc.memalign == NULL) {
382 lookup_all_symbols();
383 if (cur_alloc.memalign == NULL) {
9d34b226 384 fprintf(stderr, "memalignwrap: unable to find memalign\n");
2594a5b4 385 abort();
9d34b226
SS
386 }
387 }
2594a5b4 388 retval = cur_alloc.memalign(alignment, size);
8c06ba6f 389 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa
AB
390 tracepoint(lttng_ust_libc, memalign,
391 alignment, size, retval,
171fcc6f 392 LTTNG_UST_CALLER_IP());
8c06ba6f
MD
393 }
394 URCU_TLS(malloc_nesting)--;
9d34b226
SS
395 return retval;
396}
397
398int posix_memalign(void **memptr, size_t alignment, size_t size)
399{
9d34b226
SS
400 int retval;
401
8c06ba6f 402 URCU_TLS(malloc_nesting)++;
2594a5b4
MD
403 if (cur_alloc.posix_memalign == NULL) {
404 lookup_all_symbols();
405 if (cur_alloc.posix_memalign == NULL) {
9d34b226 406 fprintf(stderr, "posix_memalignwrap: unable to find posix_memalign\n");
2594a5b4 407 abort();
9d34b226
SS
408 }
409 }
2594a5b4 410 retval = cur_alloc.posix_memalign(memptr, alignment, size);
8c06ba6f 411 if (URCU_TLS(malloc_nesting) == 1) {
6d4658aa
AB
412 tracepoint(lttng_ust_libc, posix_memalign,
413 *memptr, alignment, size,
171fcc6f 414 retval, LTTNG_UST_CALLER_IP());
8c06ba6f
MD
415 }
416 URCU_TLS(malloc_nesting)--;
9d34b226
SS
417 return retval;
418}
2594a5b4
MD
419
420__attribute__((constructor))
421void lttng_ust_malloc_wrapper_init(void)
422{
423 /* Initialization already done */
424 if (cur_alloc.calloc) {
425 return;
426 }
427 /*
428 * Ensure the allocator is in place before the process becomes
429 * multithreaded.
430 */
431 lookup_all_symbols();
432}
This page took 0.048901 seconds and 4 git commands to generate.