Skip to content

Commit 6d6ae19

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 6d6ae19

File tree

6 files changed

+196
-16
lines changed

6 files changed

+196
-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: 142 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,86 @@ 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+
long threshold = utils_get_size_threshold(str_threshold);
139+
if (threshold < 0) {
140+
LOG_ERR("incorrect size threshold: %s", str_threshold);
141+
exit(-1);
142+
}
143+
144+
return (size_t)threshold;
145+
}
146+
147+
static int get_system_allocator_symbols(void) {
148+
*((void **)(&System_aligned_alloc)) =
149+
utils_get_symbol_addr(RTLD_NEXT, "aligned_alloc", NULL);
150+
*((void **)(&System_calloc)) =
151+
utils_get_symbol_addr(RTLD_NEXT, "calloc", NULL);
152+
*((void **)(&System_free)) = utils_get_symbol_addr(RTLD_NEXT, "free", NULL);
153+
*((void **)(&System_malloc)) =
154+
utils_get_symbol_addr(RTLD_NEXT, "malloc", NULL);
155+
*((void **)(&System_malloc_usable_size)) =
156+
utils_get_symbol_addr(RTLD_NEXT, "malloc_usable_size", NULL);
157+
*((void **)(&System_realloc)) =
158+
utils_get_symbol_addr(RTLD_NEXT, "realloc", NULL);
159+
160+
if (System_aligned_alloc && System_calloc && System_free && System_malloc &&
161+
System_malloc_usable_size && System_realloc) {
162+
return 0;
163+
}
164+
165+
*((void **)(&System_aligned_alloc)) = NULL;
166+
*((void **)(&System_calloc)) = NULL;
167+
*((void **)(&System_free)) = NULL;
168+
*((void **)(&System_malloc)) = NULL;
169+
*((void **)(&System_malloc_usable_size)) = NULL;
170+
*((void **)(&System_realloc)) = NULL;
171+
172+
return -1;
173+
}
174+
#endif /* _WIN32 */
175+
110176
void proxy_lib_create_common(void) {
111177
utils_log_init();
112178
umf_os_memory_provider_params_t os_params =
113179
umfOsMemoryProviderParamsDefault();
114180
umf_result_t umf_result;
115181

116182
#ifndef _WIN32
117-
char shm_name[NAME_MAX];
183+
size_t _threshold = get_size_threshold();
184+
if (_threshold > 0) {
185+
if (get_system_allocator_symbols()) {
186+
LOG_ERR("initialization of the system allocator failed!");
187+
exit(-1);
188+
}
189+
190+
Size_threshold_value = _threshold;
191+
LOG_INFO("system allocator initialized, size threshold value = %zu",
192+
Size_threshold_value);
193+
}
118194

119195
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");
196+
LOG_INFO("proxy_lib: using the MAP_SHARED visibility mode with the "
197+
"file descriptor duplication");
122198
os_params.visibility = UMF_MEM_MAP_SHARED;
123199
os_params.shm_name = NULL;
124200

125201
} else if (utils_env_var_has_str("UMF_PROXY",
126202
"page.disposition=shared-shm")) {
127203
os_params.visibility = UMF_MEM_MAP_SHARED;
128204

205+
char shm_name[NAME_MAX];
129206
memset(shm_name, 0, NAME_MAX);
130207
sprintf(shm_name, "umf_proxy_lib_shm_pid_%i", utils_getpid());
131208
os_params.shm_name = shm_name;
132209

133-
LOG_DEBUG("proxy_lib: using the MAP_SHARED visibility mode with the "
134-
"named shared memory: %s",
135-
os_params.shm_name);
210+
LOG_INFO("proxy_lib: using the MAP_SHARED visibility mode with the "
211+
"named shared memory: %s",
212+
os_params.shm_name);
136213
}
137-
#endif
214+
#endif /* _WIN32 */
138215

139216
umf_result = umfMemoryProviderCreate(umfOsMemoryProviderOps(), &os_params,
140217
&OS_memory_provider);
@@ -149,16 +226,18 @@ void proxy_lib_create_common(void) {
149226
LOG_ERR("creating UMF pool manager failed");
150227
exit(-1);
151228
}
229+
152230
// The UMF pool has just been created (Proxy_pool != NULL). Stop using
153231
// the linear allocator and start using the UMF pool allocator from now on.
232+
LOG_DEBUG("proxy library initialized");
154233
}
155234

156235
void proxy_lib_destroy_common(void) {
157236
if (utils_is_running_in_proxy_lib()) {
158237
// We cannot destroy 'Base_alloc_leak' nor 'Proxy_pool' nor 'OS_memory_provider',
159238
// because it could lead to use-after-free in the program's unloader
160239
// (for example _dl_fini() on Linux).
161-
return;
240+
goto fini_proxy_lib_destroy_common;
162241
}
163242

164243
umf_memory_pool_handle_t pool = Proxy_pool;
@@ -168,6 +247,10 @@ void proxy_lib_destroy_common(void) {
168247
umf_memory_provider_handle_t provider = OS_memory_provider;
169248
OS_memory_provider = NULL;
170249
umfMemoryProviderDestroy(provider);
250+
LOG_DEBUG("proxy library destroyed");
251+
252+
fini_proxy_lib_destroy_common:
253+
LOG_DEBUG("proxy library finalized");
171254
}
172255

173256
/*****************************************************************************/
@@ -246,6 +329,12 @@ static inline size_t ba_leak_pool_contains_pointer(void *ptr) {
246329
/*****************************************************************************/
247330

248331
void *malloc(size_t size) {
332+
#ifndef _WIN32
333+
if (size < Size_threshold_value) {
334+
return System_malloc(size);
335+
}
336+
#endif /* _WIN32 */
337+
249338
if (!was_called_from_umfPool && Proxy_pool) {
250339
was_called_from_umfPool = 1;
251340
void *ptr = umfPoolMalloc(Proxy_pool, size);
@@ -257,6 +346,12 @@ void *malloc(size_t size) {
257346
}
258347

259348
void *calloc(size_t nmemb, size_t size) {
349+
#ifndef _WIN32
350+
if ((nmemb * size) < Size_threshold_value) {
351+
return System_calloc(nmemb, size);
352+
}
353+
#endif /* _WIN32 */
354+
260355
if (!was_called_from_umfPool && Proxy_pool) {
261356
was_called_from_umfPool = 1;
262357
void *ptr = umfPoolCalloc(Proxy_pool, nmemb, size);
@@ -276,15 +371,22 @@ void free(void *ptr) {
276371
return;
277372
}
278373

279-
if (Proxy_pool) {
374+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
280375
if (umfPoolFree(Proxy_pool, ptr) != UMF_RESULT_SUCCESS) {
281376
LOG_ERR("umfPoolFree() failed");
282-
assert(0);
283377
}
284378
return;
285379
}
286380

287-
assert(0);
381+
#ifndef _WIN32
382+
if (Size_threshold_value) {
383+
System_free(ptr);
384+
return;
385+
}
386+
#endif /* _WIN32 */
387+
388+
LOG_ERR("free() failed: %p", ptr);
389+
288390
return;
289391
}
290392

@@ -303,18 +405,31 @@ void *realloc(void *ptr, size_t size) {
303405
return ba_leak_realloc(ptr, size, leak_pool_contains_pointer);
304406
}
305407

306-
if (Proxy_pool) {
408+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
307409
was_called_from_umfPool = 1;
308410
void *new_ptr = umfPoolRealloc(Proxy_pool, ptr, size);
309411
was_called_from_umfPool = 0;
310412
return new_ptr;
311413
}
312414

313-
assert(0);
415+
#ifndef _WIN32
416+
if (Size_threshold_value) {
417+
return System_realloc(ptr, size);
418+
}
419+
#endif /* _WIN32 */
420+
421+
LOG_ERR("realloc() failed: %p", ptr);
422+
314423
return NULL;
315424
}
316425

317426
void *aligned_alloc(size_t alignment, size_t size) {
427+
#ifndef _WIN32
428+
if (size < Size_threshold_value) {
429+
return System_aligned_alloc(alignment, size);
430+
}
431+
#endif /* _WIN32 */
432+
318433
if (!was_called_from_umfPool && Proxy_pool) {
319434
was_called_from_umfPool = 1;
320435
void *ptr = umfPoolAlignedMalloc(Proxy_pool, size, alignment);
@@ -330,19 +445,30 @@ size_t _msize(void *ptr) {
330445
#else
331446
size_t malloc_usable_size(void *ptr) {
332447
#endif
333-
334-
// a check to verify we are running the proxy library
448+
// a check to verify if we are running the proxy library
335449
if (ptr == (void *)0x01) {
336450
return 0xDEADBEEF;
337451
}
338452

339-
if (!was_called_from_umfPool && Proxy_pool) {
453+
if (ba_leak_pool_contains_pointer(ptr)) {
454+
return 0; // unsupported in case of the ba_leak allocator
455+
}
456+
457+
if (Proxy_pool && (umfPoolByPtr(ptr) == Proxy_pool)) {
340458
was_called_from_umfPool = 1;
341459
size_t size = umfPoolMallocUsableSize(Proxy_pool, ptr);
342460
was_called_from_umfPool = 0;
343461
return size;
344462
}
345463

464+
#ifndef _WIN32
465+
if (Size_threshold_value) {
466+
return System_malloc_usable_size(ptr);
467+
}
468+
#endif /* _WIN32 */
469+
470+
LOG_ERR("malloc_usable_size() failed: %p", ptr);
471+
346472
return 0; // unsupported in this case
347473
}
348474

src/utils/utils_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ int utils_file_open_or_create(const char *path);
168168

169169
int utils_fallocate(int fd, long offset, long len);
170170

171+
long utils_get_size_threshold(char *str_threshold);
172+
171173
#ifdef __cplusplus
172174
}
173175
#endif

src/utils/utils_posix_common.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*
88
*/
99

10+
#include <ctype.h>
1011
#include <errno.h>
1112
#include <fcntl.h>
1213
#include <limits.h>
@@ -266,3 +267,26 @@ int utils_file_open_or_create(const char *path) {
266267

267268
return fd;
268269
}
270+
271+
// Expected input:
272+
// char *str_threshold = utils_env_var_get_str("UMF_PROXY", "size.threshold=");
273+
long utils_get_size_threshold(char *str_threshold) {
274+
if (!str_threshold) {
275+
return 0;
276+
}
277+
278+
// move to the beginning of the number
279+
str_threshold += strlen("size.threshold=");
280+
281+
// check if the first character is a digit
282+
if (!isdigit(str_threshold[0])) {
283+
LOG_ERR("incorrect size threshold: %s", str_threshold);
284+
return -1;
285+
}
286+
287+
size_t threshold = (size_t)atol(str_threshold);
288+
LOG_DEBUG("Size_threshold_value = (char *) %s, (int) %zu", str_threshold,
289+
threshold);
290+
291+
return threshold;
292+
}

src/utils/utils_windows_common.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,10 @@ int utils_fallocate(int fd, long offset, long len) {
215215

216216
return -1;
217217
}
218+
219+
// Expected input:
220+
// char *str_threshold = utils_env_var_get_str("UMF_PROXY", "size.threshold=");
221+
long utils_get_size_threshold(char *str_threshold) {
222+
(void)str_threshold; // unused
223+
return 0;
224+
}

test/utils/utils_linux.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,17 @@ TEST_F(test, utils_shm_create_invalid_args) {
5656
ret = utils_shm_create("/abc", -1);
5757
EXPECT_EQ(ret, -1);
5858
}
59+
60+
TEST_F(test, utils_get_size_threshold) {
61+
// Expected input to utils_get_size_threshold():
62+
// char *str_threshold = utils_env_var_get_str("UMF_PROXY", "size.threshold=");
63+
// positive tests
64+
EXPECT_EQ(utils_get_size_threshold((char *)"size.threshold=111"), 111);
65+
EXPECT_EQ(utils_get_size_threshold((char *)"size.threshold=222;abcd"), 222);
66+
EXPECT_EQ(utils_get_size_threshold((char *)"size.threshold=333;var=value"),
67+
333);
68+
// negative tests
69+
EXPECT_EQ(utils_get_size_threshold(NULL), 0);
70+
EXPECT_EQ(utils_get_size_threshold((char *)"size.threshold=abc"), -1);
71+
EXPECT_EQ(utils_get_size_threshold((char *)"size.threshold=-111"), -1);
72+
}

0 commit comments

Comments
 (0)