Skip to content

Add gc and shutdown callbacks to ZendMM custom handlers #13432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions Zend/zend_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ struct _zend_mm_heap {
void *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
void *(*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
size_t (*_gc)(void);
void (*_shutdown)(bool full, bool silent);
} custom_heap;
HashTable *tracked_allocs;
#endif
Expand Down Expand Up @@ -2119,6 +2121,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap)

#if ZEND_MM_CUSTOM
if (heap->use_custom_heap) {
size_t (*gc)(void) = heap->custom_heap._gc;
if (gc) {
return gc();
}
return 0;
}
#endif
Expand Down Expand Up @@ -2421,10 +2427,10 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)

#if ZEND_MM_CUSTOM
static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
static void tracked_free_all(void);
static void tracked_free_all(zend_mm_heap *heap);
#endif

void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
{
zend_mm_chunk *p;
zend_mm_huge_list *list;
Expand All @@ -2433,7 +2439,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
if (heap->use_custom_heap) {
if (heap->custom_heap._malloc == tracked_malloc) {
if (silent) {
tracked_free_all();
tracked_free_all(heap);
}
zend_hash_clean(heap->tracked_allocs);
if (full) {
Expand All @@ -2445,9 +2451,16 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
heap->size = 0;
}

void (*shutdown)(bool, bool) = heap->custom_heap._shutdown;

if (full) {
heap->custom_heap._free(heap ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC);
}

if (shutdown) {
shutdown(full, silent);
}

return;
}
#endif
Expand Down Expand Up @@ -3039,8 +3052,8 @@ static void *tracked_realloc(void *ptr, size_t new_size ZEND_FILE_LINE_DC ZEND_F
return ptr;
}

static void tracked_free_all(void) {
HashTable *tracked_allocs = AG(mm_heap)->tracked_allocs;
static void tracked_free_all(zend_mm_heap *heap) {
HashTable *tracked_allocs = heap->tracked_allocs;
zend_ulong h;
ZEND_HASH_FOREACH_NUM_KEY(tracked_allocs, h) {
void *ptr = (void *) (uintptr_t) (h << ZEND_MM_ALIGNMENT_LOG2);
Expand Down Expand Up @@ -3138,6 +3151,18 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC))
{
#if ZEND_MM_CUSTOM
zend_mm_set_custom_handlers_ex(heap, _malloc, _free, _realloc, NULL, NULL);
#endif
}

ZEND_API void zend_mm_set_custom_handlers_ex(zend_mm_heap *heap,
void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
size_t (*_gc)(void),
void (*_shutdown)(bool, bool))
{
#if ZEND_MM_CUSTOM
zend_mm_heap *_heap = (zend_mm_heap*)heap;

Expand All @@ -3148,14 +3173,28 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
_heap->custom_heap._malloc = _malloc;
_heap->custom_heap._free = _free;
_heap->custom_heap._realloc = _realloc;
_heap->custom_heap._gc = _gc;
_heap->custom_heap._shutdown = _shutdown;
}
#endif
}

ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap,
void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC))
void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC))
{
#if ZEND_MM_CUSTOM
zend_mm_get_custom_handlers_ex(heap, _malloc, _free, _realloc, NULL, NULL);
#endif
}

ZEND_API void zend_mm_get_custom_handlers_ex(zend_mm_heap *heap,
void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
size_t (**_gc)(void),
void (**_shutdown)(bool, bool))
{
#if ZEND_MM_CUSTOM
zend_mm_heap *_heap = (zend_mm_heap*)heap;
Expand All @@ -3164,15 +3203,29 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap,
*_malloc = _heap->custom_heap._malloc;
*_free = _heap->custom_heap._free;
*_realloc = _heap->custom_heap._realloc;
if (_gc != NULL) {
*_gc = _heap->custom_heap._gc;
}
if (_shutdown != NULL) {
*_shutdown = _heap->custom_heap._shutdown;
}
} else {
*_malloc = NULL;
*_free = NULL;
*_realloc = NULL;
if (_gc != NULL) {
*_gc = NULL;
}
if (_shutdown != NULL) {
*_shutdown = NULL;
}
}
#else
*_malloc = NULL;
*_free = NULL;
*_realloc = NULL;
*_gc = NULL;
*_shutdown = NULL;
#endif
}

Expand Down
24 changes: 18 additions & 6 deletions Zend/zend_alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,25 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap);

ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap);
ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap,
void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC));
void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC));
ZEND_API void zend_mm_set_custom_handlers_ex(zend_mm_heap *heap,
void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (*_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (*_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
size_t (*_gc)(void),
void (*_shutdown)(bool, bool));
ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap,
void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC));
void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC));
ZEND_API void zend_mm_get_custom_handlers_ex(zend_mm_heap *heap,
void* (**_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void (**_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
void* (**_realloc)(void*, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC),
size_t (**_gc)(void),
void (**_shutdown)(bool, bool));

typedef struct _zend_mm_storage zend_mm_storage;

Expand Down
2 changes: 1 addition & 1 deletion ext/zend_test/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ PHP_ARG_ENABLE([zend-test],
[Enable zend_test extension])])

if test "$PHP_ZEND_TEST" != "no"; then
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_NEW_EXTENSION(zend_test, test.c observer.c fiber.c iterators.c object_handlers.c zend_mm_custom_handlers.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi
2 changes: 1 addition & 1 deletion ext/zend_test/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
ARG_ENABLE("zend-test", "enable zend_test extension", "no");

if (PHP_ZEND_TEST != "no") {
EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
EXTENSION("zend_test", "test.c observer.c fiber.c iterators.c object_handlers.c zend_mm_custom_handlers.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS ");
}
13 changes: 13 additions & 0 deletions ext/zend_test/php_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
zend_long quantity_value;
zend_string *str_test;
zend_string *not_empty_str_test;
int zend_mm_custom_handlers_enabled;

// the previous heap that was found in ZendMM
zend_mm_heap* original_heap;
// the custom handlers that might have been found in the previous heap
void* (*custom_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
void (*custom_free)(void* ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
void* (*custom_realloc)(void *, size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC);
size_t (*custom_gc)(void);
void (*custom_shutdown)(bool, bool);
// this is our heap that we install our custom handlers on and inject into
// ZendMM
zend_mm_heap* observed_heap;
ZEND_END_MODULE_GLOBALS(zend_test)

extern ZEND_DECLARE_MODULE_GLOBALS(zend_test)
Expand Down
5 changes: 5 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
+----------------------------------------------------------------------+
*/

#include "zend_modules.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
Expand All @@ -35,6 +36,7 @@
#include "test_arginfo.h"
#include "zend_call_stack.h"
#include "zend_exceptions.h"
#include "zend_mm_custom_handlers.h"

// `php.h` sets `NDEBUG` when not `PHP_DEBUG` which will make `assert()` from
// assert.h a no-op. In order to have `assert()` working on NDEBUG builds, we
Expand Down Expand Up @@ -1260,6 +1262,7 @@ PHP_MINIT_FUNCTION(zend_test)
}

zend_test_observer_init(INIT_FUNC_ARGS_PASSTHRU);
zend_test_mm_custom_handlers_minit(INIT_FUNC_ARGS_PASSTHRU);
zend_test_fiber_init();
zend_test_iterators_init();
zend_test_object_handlers_init();
Expand Down Expand Up @@ -1288,6 +1291,7 @@ PHP_RINIT_FUNCTION(zend_test)
{
zend_hash_init(&ZT_G(global_weakmap), 8, NULL, ZVAL_PTR_DTOR, 0);
ZT_G(observer_nesting_depth) = 0;
zend_test_mm_custom_handlers_rinit();
return SUCCESS;
}

Expand All @@ -1305,6 +1309,7 @@ PHP_RSHUTDOWN_FUNCTION(zend_test)
zend_mm_set_heap(ZT_G(zend_orig_heap));
}

zend_test_mm_custom_handlers_rshutdown();
return SUCCESS;
}

Expand Down
12 changes: 12 additions & 0 deletions ext/zend_test/tests/zend_mm_custom_handlers_alloc_01.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
ZendMM Custom Handlers: garbage collection
--EXTENSIONS--
zend_test
--FILE--
<?php
ini_set('zend_test.zend_mm_custom_handlers.enabled', 1);
$string = str_repeat('String', rand(1,100));
ini_set('zend_test.zend_mm_custom_handlers.enabled', 0);
?>
--EXPECTREGEX--
.*Allocated \d+ bytes at.*
13 changes: 13 additions & 0 deletions ext/zend_test/tests/zend_mm_custom_handlers_free_01.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
ZendMM Custom Handlers: garbage collection
--EXTENSIONS--
zend_test
--FILE--
<?php
$string = str_repeat('String', rand(1,100));
ini_set('zend_test.zend_mm_custom_handlers.enabled', 1);
unset($string);
ini_set('zend_test.zend_mm_custom_handlers.enabled', 0);
?>
--EXPECTREGEX--
.*Freed memory at.*
12 changes: 12 additions & 0 deletions ext/zend_test/tests/zend_mm_custom_handlers_gc_01.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
ZendMM Custom Handlers: garbage collection
--EXTENSIONS--
zend_test
--FILE--
<?php
ini_set('zend_test.zend_mm_custom_handlers.enabled', 1);
gc_mem_caches();
ini_set('zend_test.zend_mm_custom_handlers.enabled', 0);
?>
--EXPECTREGEX--
.*ZendMM GC freed \d+ bytes.*
Loading
Loading