Skip to content

Commit 8137e52

Browse files
committed
Add size threshold to proxy lib to call system allocator (Linux only)
Add a size threshold to proxy lib to call system allocator when the size is less than the given threshold (Linux only yet). Signed-off-by: Lukasz Dorau <lukasz.dorau@intel.com>
1 parent 2172179 commit 8137e52

File tree

2 files changed

+167
-16
lines changed

2 files changed

+167
-16
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,13 @@ The memory used by the proxy memory allocator is mmap'ed:
317317
- `page.disposition=shared-shm` - IPC uses the named shared memory. An SHM name is generated using the `umf_proxy_lib_shm_pid_$PID` pattern, where `$PID` is the PID of the process. It creates the `/dev/shm/umf_proxy_lib_shm_pid_$PID` file.
318318
- `page.disposition=shared-fd` - IPC uses the file descriptor duplication. It requires using `pidfd_getfd(2)` to obtain a duplicate of another process's file descriptor. Permission to duplicate another process's file descriptor is governed by a ptrace access mode `PTRACE_MODE_ATTACH_REALCREDS` check (see `ptrace(2)`) that can be changed using the `/proc/sys/kernel/yama/ptrace_scope` interface. `pidfd_getfd(2)` is supported since Linux 5.6.
319319

320+
**Size threshold**
321+
322+
The **size threshold** feature (Linux only) causes that all allocations of size less than the given threshold value go to the default system allocator instead of the proxy library.
323+
It can be enabled by adding the `size.threshold=<value>` string to the `UMF_PROXY` environment variable (with `';'` as a separator), for example: `UMF_PROXY="page.disposition=shared-shm;size.threshold=64"`.
324+
325+
**Remark:** changing a size of allocation (using `realloc()` ) does not change the allocator (`realloc(malloc(threshold - 1), threshold + 1)` still belongs to the default system allocator and `realloc(malloc(threshold + 1), threshold - 1)` still belongs to the proxy library pool allocator).
326+
320327
#### Windows
321328

322329
In case of Windows it requires:

src/proxy_lib/proxy_lib.c

Lines changed: 160 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
* - _aligned_offset_recalloc()
2828
*/
2929

30+
#ifndef _WIN32
31+
#define _GNU_SOURCE // for RTLD_NEXT
32+
#include <dlfcn.h>
33+
#undef _GNU_SOURCE
34+
#endif /* _WIN32 */
35+
3036
#if (defined PROXY_LIB_USES_JEMALLOC_POOL)
3137
#include <umf/pools/pool_jemalloc.h>
3238
#define umfPoolManagerOps umfJemallocPoolOps
@@ -48,6 +54,7 @@
4854
#include "base_alloc_linear.h"
4955
#include "proxy_lib.h"
5056
#include "utils_common.h"
57+
#include "utils_load_library.h"
5158
#include "utils_log.h"
5259

5360
#ifdef _WIN32 /* Windows ***************************************/
@@ -94,6 +101,24 @@ void utils_init_once(UTIL_ONCE_FLAG *flag, void (*onceCb)(void));
94101
* of a UMF pool to allocate memory needed by an application. It should be freed
95102
* by an application.
96103
*/
104+
#ifndef _WIN32
105+
typedef void *(*system_aligned_alloc_t)(size_t alignment, size_t size);
106+
typedef void *(*system_calloc_t)(size_t nmemb, size_t size);
107+
typedef void (*system_free_t)(void *ptr);
108+
typedef void *(*system_malloc_t)(size_t size);
109+
typedef size_t (*system_malloc_usable_size_t)(void *ptr);
110+
typedef void *(*system_realloc_t)(void *ptr, size_t size);
111+
112+
// pointers to the default system allocator's API
113+
static system_aligned_alloc_t System_aligned_alloc;
114+
static system_calloc_t System_calloc;
115+
static system_free_t System_free;
116+
static system_malloc_t System_malloc;
117+
static system_malloc_usable_size_t System_malloc_usable_size;
118+
static system_realloc_t System_realloc;
119+
120+
static size_t Size_threshold_value = 0;
121+
#endif /* _WIN32 */
97122

98123
static UTIL_ONCE_FLAG Base_alloc_leak_initialized = UTIL_ONCE_FLAG_INIT;
99124
static umf_ba_linear_pool_t *Base_alloc_leak = NULL;
@@ -107,34 +132,104 @@ static __TLS int was_called_from_umfPool = 0;
107132
/*** The constructor and destructor of the proxy library *********************/
108133
/*****************************************************************************/
109134

135+
#ifndef _WIN32
136+
static size_t get_size_threshold(void) {
137+
char *str_threshold = utils_env_var_get_str("UMF_PROXY", "size.threshold=");
138+
if (!str_threshold) {
139+
return 0;
140+
}
141+
142+
// move to the beginning of the number
143+
str_threshold += strlen("size.threshold=");
144+
// find ';' at the end
145+
char *end = strstr(str_threshold, ";");
146+
if (end) {
147+
// replace ';' with '\0' to mark end of the string
148+
*end = '\0';
149+
}
150+
151+
long long_threshold = atol(str_threshold);
152+
if (long_threshold < 0) {
153+
LOG_ERR("size threshold cannot be negative! = (char *) %s, (int) %li",
154+
str_threshold, long_threshold);
155+
return 0;
156+
}
157+
158+
size_t int_threshold = (size_t)long_threshold;
159+
LOG_DEBUG("Size_threshold_value = (char *) %s, (int) %zu", str_threshold,
160+
int_threshold);
161+
162+
return int_threshold;
163+
}
164+
165+
static int get_system_allocator_symbols(void) {
166+
*((void **)(&System_aligned_alloc)) =
167+
utils_get_symbol_addr(RTLD_NEXT, "aligned_alloc", NULL);
168+
*((void **)(&System_calloc)) =
169+
utils_get_symbol_addr(RTLD_NEXT, "calloc", NULL);
170+
*((void **)(&System_free)) = utils_get_symbol_addr(RTLD_NEXT, "free", NULL);
171+
*((void **)(&System_malloc)) =
172+
utils_get_symbol_addr(RTLD_NEXT, "malloc", NULL);
173+
*((void **)(&System_malloc_usable_size)) =
174+
utils_get_symbol_addr(RTLD_NEXT, "malloc_usable_size", NULL);
175+
*((void **)(&System_realloc)) =
176+
utils_get_symbol_addr(RTLD_NEXT, "realloc", NULL);
177+
178+
if (System_aligned_alloc && System_calloc && System_free && System_malloc &&
179+
System_malloc_usable_size && System_realloc) {
180+
return 0;
181+
}
182+
183+
*((void **)(&System_aligned_alloc)) = NULL;
184+
*((void **)(&System_calloc)) = NULL;
185+
*((void **)(&System_free)) = NULL;
186+
*((void **)(&System_malloc)) = NULL;
187+
*((void **)(&System_malloc_usable_size)) = NULL;
188+
*((void **)(&System_realloc)) = NULL;
189+
190+
return -1;
191+
}
192+
#endif /* _WIN32 */
193+
110194
void proxy_lib_create_common(void) {
111195
utils_log_init();
112196
umf_os_memory_provider_params_t os_params =
113197
umfOsMemoryProviderParamsDefault();
114198
umf_result_t umf_result;
115199

116200
#ifndef _WIN32
117-
char shm_name[NAME_MAX];
201+
size_t _threshold = get_size_threshold();
202+
if (_threshold > 0) {
203+
if (get_system_allocator_symbols()) {
204+
LOG_ERR("initialization of the system allocator failed!");
205+
exit(-1);
206+
}
207+
208+
Size_threshold_value = _threshold;
209+
LOG_INFO("system allocator initialized, size threshold value = %zu",
210+
Size_threshold_value);
211+
}
118212

119213
if (utils_env_var_has_str("UMF_PROXY", "page.disposition=shared-fd")) {
120-
LOG_DEBUG("proxy_lib: using the MAP_SHARED visibility mode with the "
121-
"file descriptor duplication");
214+
LOG_INFO("proxy_lib: using the MAP_SHARED visibility mode with the "
215+
"file descriptor duplication");
122216
os_params.visibility = UMF_MEM_MAP_SHARED;
123217
os_params.shm_name = NULL;
124218

125219
} else if (utils_env_var_has_str("UMF_PROXY",
126220
"page.disposition=shared-shm")) {
127221
os_params.visibility = UMF_MEM_MAP_SHARED;
128222

223+
char shm_name[NAME_MAX];
129224
memset(shm_name, 0, NAME_MAX);
130225
sprintf(shm_name, "umf_proxy_lib_shm_pid_%i", utils_getpid());
131226
os_params.shm_name = shm_name;
132227

133-
LOG_DEBUG("proxy_lib: using the MAP_SHARED visibility mode with the "
134-
"named shared memory: %s",
135-
os_params.shm_name);
228+
LOG_INFO("proxy_lib: using the MAP_SHARED visibility mode with the "
229+
"named shared memory: %s",
230+
os_params.shm_name);
136231
}
137-
#endif
232+
#endif /* _WIN32 */
138233

139234
umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), &os_params,
140235
&OS_memory_provider);
@@ -149,16 +244,18 @@ void proxy_lib_create_common(void) {
149244
LOG_ERR("creating UMF pool manager failed");
150245
exit(-1);
151246
}
247+
152248
// The UMF pool has just been created (Proxy_pool != NULL). Stop using
153249
// the linear allocator and start using the UMF pool allocator from now on.
250+
LOG_DEBUG("proxy library initialized");
154251
}
155252

156253
void proxy_lib_destroy_common(void) {
157254
if (utils_is_running_in_proxy_lib()) {
158255
// We cannot destroy 'Base_alloc_leak' nor 'Proxy_pool' nor 'OS_memory_provider',
159256
// because it could lead to use-after-free in the program's unloader
160257
// (for example _dl_fini() on Linux).
161-
return;
258+
goto fini_proxy_lib_destroy_common;
162259
}
163260

164261
umf_memory_pool_handle_t pool = Proxy_pool;
@@ -168,6 +265,10 @@ void proxy_lib_destroy_common(void) {
168265
umf_memory_provider_handle_t provider = OS_memory_provider;
169266
OS_memory_provider = NULL;
170267
umfMemoryProviderDestroy(provider);
268+
LOG_DEBUG("proxy library destroyed");
269+
270+
fini_proxy_lib_destroy_common:
271+
LOG_DEBUG("proxy library finalized");
171272
}
172273

173274
/*****************************************************************************/
@@ -246,6 +347,12 @@ static inline size_t ba_leak_pool_contains_pointer(void *ptr) {
246347
/*****************************************************************************/
247348

248349
void *malloc(size_t size) {
350+
#ifndef _WIN32
351+
if (size < Size_threshold_value) {
352+
return System_malloc(size);
353+
}
354+
#endif /* _WIN32 */
355+
249356
if (!was_called_from_umfPool && Proxy_pool) {
250357
was_called_from_umfPool = 1;
251358
void *ptr = umfPoolMalloc(Proxy_pool, size);
@@ -257,6 +364,12 @@ void *malloc(size_t size) {
257364
}
258365

259366
void *calloc(size_t nmemb, size_t size) {
367+
#ifndef _WIN32
368+
if ((nmemb * size) < Size_threshold_value) {
369+
return System_calloc(nmemb, size);
370+
}
371+
#endif /* _WIN32 */
372+
260373
if (!was_called_from_umfPool && Proxy_pool) {
261374
was_called_from_umfPool = 1;
262375
void *ptr = umfPoolCalloc(Proxy_pool, nmemb, size);
@@ -276,15 +389,22 @@ void free(void *ptr) {
276389
return;
277390
}
278391

279-
if (Proxy_pool) {
392+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
280393
if (umfPoolFree(Proxy_pool, ptr) != UMF_RESULT_SUCCESS) {
281394
LOG_ERR("umfPoolFree() failed");
282-
assert(0);
283395
}
284396
return;
285397
}
286398

287-
assert(0);
399+
#ifndef _WIN32
400+
if (Size_threshold_value) {
401+
System_free(ptr);
402+
return;
403+
}
404+
#endif /* _WIN32 */
405+
406+
LOG_ERR("free() failed: %p", ptr);
407+
288408
return;
289409
}
290410

@@ -303,18 +423,31 @@ void *realloc(void *ptr, size_t size) {
303423
return ba_leak_realloc(ptr, size, leak_pool_contains_pointer);
304424
}
305425

306-
if (Proxy_pool) {
426+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
307427
was_called_from_umfPool = 1;
308428
void *new_ptr = umfPoolRealloc(Proxy_pool, ptr, size);
309429
was_called_from_umfPool = 0;
310430
return new_ptr;
311431
}
312432

313-
assert(0);
433+
#ifndef _WIN32
434+
if (Size_threshold_value) {
435+
return System_realloc(ptr, size);
436+
}
437+
#endif /* _WIN32 */
438+
439+
LOG_ERR("realloc() failed: %p", ptr);
440+
314441
return NULL;
315442
}
316443

317444
void *aligned_alloc(size_t alignment, size_t size) {
445+
#ifndef _WIN32
446+
if (size < Size_threshold_value) {
447+
return System_aligned_alloc(alignment, size);
448+
}
449+
#endif /* _WIN32 */
450+
318451
if (!was_called_from_umfPool && Proxy_pool) {
319452
was_called_from_umfPool = 1;
320453
void *ptr = umfPoolAlignedMalloc(Proxy_pool, size, alignment);
@@ -330,19 +463,30 @@ size_t _msize(void *ptr) {
330463
#else
331464
size_t malloc_usable_size(void *ptr) {
332465
#endif
333-
334-
// a check to verify we are running the proxy library
466+
// a check to verify if we are running the proxy library
335467
if (ptr == (void *)0x01) {
336468
return 0xDEADBEEF;
337469
}
338470

339-
if (!was_called_from_umfPool && Proxy_pool) {
471+
if (ba_leak_pool_contains_pointer(ptr)) {
472+
return 0; // unsupported in case of the ba_leak allocator
473+
}
474+
475+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
340476
was_called_from_umfPool = 1;
341477
size_t size = umfPoolMallocUsableSize(Proxy_pool, ptr);
342478
was_called_from_umfPool = 0;
343479
return size;
344480
}
345481

482+
#ifndef _WIN32
483+
if (Size_threshold_value) {
484+
return System_malloc_usable_size(ptr);
485+
}
486+
#endif /* _WIN32 */
487+
488+
LOG_ERR("malloc_usable_size() failed: %p", ptr);
489+
346490
return 0; // unsupported in this case
347491
}
348492

0 commit comments

Comments
 (0)