Skip to content

Commit 36eca5a

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 e02670e commit 36eca5a

File tree

2 files changed

+156
-16
lines changed

2 files changed

+156
-16
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ 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+
The **size threshold** feature (Linux only) 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"`.
321+
It causes that all allocations of size less than the given threshold value go to the default system allocator instead of the proxy library.
322+
320323
#### Windows
321324

322325
In case of Windows it requires:

src/proxy_lib/proxy_lib.c

Lines changed: 153 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,97 @@ 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+
size_t int_threshold = (size_t)atoi(str_threshold);
152+
LOG_DEBUG("Size_threshold_value = (char *) %s, (int) %zu", str_threshold,
153+
int_threshold);
154+
155+
return int_threshold;
156+
}
157+
158+
static int get_system_allocator_symbols(void) {
159+
*((void **)(&System_aligned_alloc)) =
160+
utils_get_symbol_addr(RTLD_NEXT, "aligned_alloc", NULL);
161+
*((void **)(&System_calloc)) =
162+
utils_get_symbol_addr(RTLD_NEXT, "calloc", NULL);
163+
*((void **)(&System_free)) = utils_get_symbol_addr(RTLD_NEXT, "free", NULL);
164+
*((void **)(&System_malloc)) =
165+
utils_get_symbol_addr(RTLD_NEXT, "malloc", NULL);
166+
*((void **)(&System_malloc_usable_size)) =
167+
utils_get_symbol_addr(RTLD_NEXT, "malloc_usable_size", NULL);
168+
*((void **)(&System_realloc)) =
169+
utils_get_symbol_addr(RTLD_NEXT, "realloc", NULL);
170+
171+
if (System_aligned_alloc && System_calloc && System_free && System_malloc &&
172+
System_malloc_usable_size && System_realloc) {
173+
return 0;
174+
}
175+
176+
*((void **)(&System_aligned_alloc)) = NULL;
177+
*((void **)(&System_calloc)) = NULL;
178+
*((void **)(&System_free)) = NULL;
179+
*((void **)(&System_malloc)) = NULL;
180+
*((void **)(&System_malloc_usable_size)) = NULL;
181+
*((void **)(&System_realloc)) = NULL;
182+
183+
return -1;
184+
}
185+
#endif /* _WIN32 */
186+
110187
void proxy_lib_create_common(void) {
111188
utils_log_init();
112189
umf_os_memory_provider_params_t os_params =
113190
umfOsMemoryProviderParamsDefault();
114191
umf_result_t umf_result;
115192

116193
#ifndef _WIN32
117-
char shm_name[NAME_MAX];
194+
size_t _threshold = get_size_threshold();
195+
if (_threshold > 0) {
196+
if (get_system_allocator_symbols()) {
197+
LOG_ERR("initialization of the system allocator failed!");
198+
exit(-1);
199+
}
200+
201+
Size_threshold_value = _threshold;
202+
LOG_INFO("system allocator initialized, size threshold value = %zu",
203+
Size_threshold_value);
204+
}
118205

119206
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");
207+
LOG_INFO("proxy_lib: using the MAP_SHARED visibility mode with the "
208+
"file descriptor duplication");
122209
os_params.visibility = UMF_MEM_MAP_SHARED;
123210
os_params.shm_name = NULL;
124211

125212
} else if (utils_env_var_has_str("UMF_PROXY",
126213
"page.disposition=shared-shm")) {
127214
os_params.visibility = UMF_MEM_MAP_SHARED;
128215

216+
char shm_name[NAME_MAX];
129217
memset(shm_name, 0, NAME_MAX);
130218
sprintf(shm_name, "umf_proxy_lib_shm_pid_%i", utils_getpid());
131219
os_params.shm_name = shm_name;
132220

133-
LOG_DEBUG("proxy_lib: using the MAP_SHARED visibility mode with the "
134-
"named shared memory: %s",
135-
os_params.shm_name);
221+
LOG_INFO("proxy_lib: using the MAP_SHARED visibility mode with the "
222+
"named shared memory: %s",
223+
os_params.shm_name);
136224
}
137-
#endif
225+
#endif /* _WIN32 */
138226

139227
umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), &os_params,
140228
&OS_memory_provider);
@@ -149,16 +237,18 @@ void proxy_lib_create_common(void) {
149237
LOG_ERR("creating UMF pool manager failed");
150238
exit(-1);
151239
}
240+
152241
// The UMF pool has just been created (Proxy_pool != NULL). Stop using
153242
// the linear allocator and start using the UMF pool allocator from now on.
243+
LOG_DEBUG("proxy library initialized");
154244
}
155245

156246
void proxy_lib_destroy_common(void) {
157247
if (utils_is_running_in_proxy_lib()) {
158248
// We cannot destroy 'Base_alloc_leak' nor 'Proxy_pool' nor 'OS_memory_provider',
159249
// because it could lead to use-after-free in the program's unloader
160250
// (for example _dl_fini() on Linux).
161-
return;
251+
goto fini_proxy_lib_destroy_common;
162252
}
163253

164254
umf_memory_pool_handle_t pool = Proxy_pool;
@@ -168,6 +258,10 @@ void proxy_lib_destroy_common(void) {
168258
umf_memory_provider_handle_t provider = OS_memory_provider;
169259
OS_memory_provider = NULL;
170260
umfMemoryProviderDestroy(provider);
261+
LOG_DEBUG("proxy library destroyed");
262+
263+
fini_proxy_lib_destroy_common:
264+
LOG_DEBUG("proxy library finalized");
171265
}
172266

173267
/*****************************************************************************/
@@ -246,6 +340,12 @@ static inline size_t ba_leak_pool_contains_pointer(void *ptr) {
246340
/*****************************************************************************/
247341

248342
void *malloc(size_t size) {
343+
#ifndef _WIN32
344+
if (size < Size_threshold_value) {
345+
return System_malloc(size);
346+
}
347+
#endif /* _WIN32 */
348+
249349
if (!was_called_from_umfPool && Proxy_pool) {
250350
was_called_from_umfPool = 1;
251351
void *ptr = umfPoolMalloc(Proxy_pool, size);
@@ -257,6 +357,12 @@ void *malloc(size_t size) {
257357
}
258358

259359
void *calloc(size_t nmemb, size_t size) {
360+
#ifndef _WIN32
361+
if ((nmemb * size) < Size_threshold_value) {
362+
return System_calloc(nmemb, size);
363+
}
364+
#endif /* _WIN32 */
365+
260366
if (!was_called_from_umfPool && Proxy_pool) {
261367
was_called_from_umfPool = 1;
262368
void *ptr = umfPoolCalloc(Proxy_pool, nmemb, size);
@@ -276,15 +382,22 @@ void free(void *ptr) {
276382
return;
277383
}
278384

279-
if (Proxy_pool) {
385+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
280386
if (umfPoolFree(Proxy_pool, ptr) != UMF_RESULT_SUCCESS) {
281387
LOG_ERR("umfPoolFree() failed");
282-
assert(0);
283388
}
284389
return;
285390
}
286391

287-
assert(0);
392+
#ifndef _WIN32
393+
if (Size_threshold_value) {
394+
System_free(ptr);
395+
return;
396+
}
397+
#endif /* _WIN32 */
398+
399+
LOG_ERR("free() failed: %p", ptr);
400+
288401
return;
289402
}
290403

@@ -303,18 +416,31 @@ void *realloc(void *ptr, size_t size) {
303416
return ba_leak_realloc(ptr, size, leak_pool_contains_pointer);
304417
}
305418

306-
if (Proxy_pool) {
419+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
307420
was_called_from_umfPool = 1;
308421
void *new_ptr = umfPoolRealloc(Proxy_pool, ptr, size);
309422
was_called_from_umfPool = 0;
310423
return new_ptr;
311424
}
312425

313-
assert(0);
426+
#ifndef _WIN32
427+
if (Size_threshold_value) {
428+
return System_realloc(ptr, size);
429+
}
430+
#endif /* _WIN32 */
431+
432+
LOG_ERR("realloc() failed: %p", ptr);
433+
314434
return NULL;
315435
}
316436

317437
void *aligned_alloc(size_t alignment, size_t size) {
438+
#ifndef _WIN32
439+
if (size < Size_threshold_value) {
440+
return System_aligned_alloc(alignment, size);
441+
}
442+
#endif /* _WIN32 */
443+
318444
if (!was_called_from_umfPool && Proxy_pool) {
319445
was_called_from_umfPool = 1;
320446
void *ptr = umfPoolAlignedMalloc(Proxy_pool, size, alignment);
@@ -330,19 +456,30 @@ size_t _msize(void *ptr) {
330456
#else
331457
size_t malloc_usable_size(void *ptr) {
332458
#endif
333-
334-
// a check to verify we are running the proxy library
459+
// a check to verify if we are running the proxy library
335460
if (ptr == (void *)0x01) {
336461
return 0xDEADBEEF;
337462
}
338463

339-
if (!was_called_from_umfPool && Proxy_pool) {
464+
if (ba_leak_pool_contains_pointer(ptr)) {
465+
return 0; // unsupported in case of the ba_leak allocator
466+
}
467+
468+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
340469
was_called_from_umfPool = 1;
341470
size_t size = umfPoolMallocUsableSize(Proxy_pool, ptr);
342471
was_called_from_umfPool = 0;
343472
return size;
344473
}
345474

475+
#ifndef _WIN32
476+
if (Size_threshold_value) {
477+
return System_malloc_usable_size(ptr);
478+
}
479+
#endif /* _WIN32 */
480+
481+
LOG_ERR("malloc_usable_size() failed: %p", ptr);
482+
346483
return 0; // unsupported in this case
347484
}
348485

0 commit comments

Comments
 (0)