From c3e9a903b8977764663dd76d4e43643b953aeae8 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 5 Jul 2023 13:27:44 +0200 Subject: [PATCH 01/26] Add ZendMM observer --- Zend/zend_alloc.c | 323 +++++++++++++++++++++++++++++++++++++++++++--- Zend/zend_alloc.h | 24 +++- main/main.c | 6 + 3 files changed, 330 insertions(+), 23 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index d76c4da023ed2..42f920db62be2 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -51,6 +51,7 @@ * with more specialized routines when the requested size is known. */ +#include "php.h" #include "zend.h" #include "zend_alloc.h" #include "zend_globals.h" @@ -234,9 +235,25 @@ static bool zend_mm_use_huge_pages = false; * 2 for 5-8, 3 for 9-16 etc) see zend_alloc_sizes.h */ + +struct _zend_mm_observer { + void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + zend_mm_observer *next; +}; +zend_mm_observer *zend_mm_observers = NULL; + +typedef struct _zend_mm_heap_observer zend_mm_heap_observer; +struct _zend_mm_heap_observer { + zend_mm_observer *observer; + bool enabled; + zend_mm_heap_observer *next; +}; + struct _zend_mm_heap { #if ZEND_MM_CUSTOM - int use_custom_heap; + int use_custom_heap; /* bitflag */ #endif #if ZEND_MM_STORAGE zend_mm_storage *storage; @@ -281,6 +298,7 @@ struct _zend_mm_heap { } debug; } custom_heap; HashTable *tracked_allocs; + zend_mm_heap_observer *observers; #endif }; @@ -1947,6 +1965,8 @@ static zend_mm_heap *zend_mm_init(void) #endif #if ZEND_MM_CUSTOM heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_NONE; + heap->observers = NULL; + zend_mm_observers_startup(heap); #endif #if ZEND_MM_STORAGE heap->storage = NULL; @@ -1967,7 +1987,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) size_t collected = 0; #if ZEND_MM_CUSTOM - if (heap->use_custom_heap) { + if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { return 0; } #endif @@ -2271,7 +2291,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) zend_mm_huge_list *list; #if ZEND_MM_CUSTOM - if (heap->use_custom_heap) { + if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { if (heap->custom_heap.std._malloc == tracked_malloc) { if (silent) { tracked_free_all(); @@ -2287,7 +2307,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) } if (full) { - if (ZEND_DEBUG && heap->use_custom_heap == ZEND_MM_CUSTOM_HEAP_DEBUG) { + if (ZEND_DEBUG && heap->use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { heap->custom_heap.debug._free(heap ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); } else { heap->custom_heap.std._free(heap); @@ -2445,16 +2465,16 @@ static zend_alloc_globals alloc_globals; ZEND_API bool is_zend_mm(void) { #if ZEND_MM_CUSTOM - return !AG(mm_heap)->use_custom_heap; + return !(AG(mm_heap)->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED); #else - return 1; + return true; #endif } ZEND_API bool is_zend_ptr(const void *ptr) { #if ZEND_MM_CUSTOM - if (AG(mm_heap)->use_custom_heap) { + if (AG(mm_heap)->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { if (AG(mm_heap)->custom_heap.std._malloc == tracked_malloc) { zend_ulong h = ((uintptr_t) ptr) >> ZEND_MM_ALIGNMENT_LOG2; zval *size_zv = zend_hash_index_find(AG(mm_heap)->tracked_allocs, h); @@ -2496,29 +2516,77 @@ ZEND_API bool is_zend_ptr(const void *ptr) static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - if (ZEND_DEBUG && AG(mm_heap)->use_custom_heap == ZEND_MM_CUSTOM_HEAP_DEBUG) { - return AG(mm_heap)->custom_heap.debug._malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + void *ptr; + zend_mm_heap *heap = AG(mm_heap); + int use_custom_heap = heap->use_custom_heap; + if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { + ptr = heap->custom_heap.debug._malloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } else if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_STD) { + ptr = heap->custom_heap.std._malloc(size); } else { - return AG(mm_heap)->custom_heap.std._malloc(size); + // no custom memory manager, only observer present + ptr = zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->enabled && current->observer != NULL && current->observer->malloc != NULL) { + current->observer->malloc(size, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + current = current->next; + } } + return ptr; } static ZEND_COLD void ZEND_FASTCALL _efree_custom(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - if (ZEND_DEBUG && AG(mm_heap)->use_custom_heap == ZEND_MM_CUSTOM_HEAP_DEBUG) { - AG(mm_heap)->custom_heap.debug._free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + zend_mm_heap *heap = AG(mm_heap); + int use_custom_heap = heap->use_custom_heap; + + if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->enabled && current->observer != NULL && current->observer->free != NULL) { + current->observer->free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + current = current->next; + } + } + + if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { + heap->custom_heap.debug._free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } else if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_STD) { + heap->custom_heap.std._free(ptr); } else { - AG(mm_heap)->custom_heap.std._free(ptr); + // no custom memory manager, only observer present + zend_mm_free_heap(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } } static ZEND_COLD void* ZEND_FASTCALL _realloc_custom(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - if (ZEND_DEBUG && AG(mm_heap)->use_custom_heap == ZEND_MM_CUSTOM_HEAP_DEBUG) { - return AG(mm_heap)->custom_heap.debug._realloc(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + void *new_ptr; + zend_mm_heap *heap = AG(mm_heap); + int use_custom_heap = heap->use_custom_heap; + if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { + new_ptr = heap->custom_heap.debug._realloc(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } else if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_STD) { + new_ptr = heap->custom_heap.std._realloc(ptr, size); } else { - return AG(mm_heap)->custom_heap.std._realloc(ptr, size); + // no custom memory manager, only observer present + new_ptr = zend_mm_realloc_heap(AG(mm_heap), ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->enabled && current->observer != NULL && current->observer->realloc != NULL) { + current->observer->realloc(ptr, size, new_ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } + current = current->next; + } } + return new_ptr; } #endif @@ -2932,6 +3000,7 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) mm_heap->tracked_allocs = malloc(sizeof(HashTable)); zend_hash_init(mm_heap->tracked_allocs, 1024, NULL, NULL, 1); } + zend_mm_observers_startup(mm_heap); return; } #endif @@ -2946,6 +3015,7 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) #ifdef ZTS static void alloc_globals_dtor(zend_alloc_globals *alloc_globals) { + zend_mm_observers_shutdown(alloc_globals->mm_heap); zend_mm_shutdown(alloc_globals->mm_heap, 1, 1); } #endif @@ -2983,9 +3053,18 @@ ZEND_API zend_mm_heap *zend_mm_get_heap(void) ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap) { #if ZEND_MM_CUSTOM - return AG(mm_heap)->use_custom_heap; + return (AG(mm_heap)->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) != 0; #else - return 0; + return false; +#endif +} + +ZEND_API bool zend_mm_is_observed(zend_mm_heap *new_heap) +{ +#if ZEND_MM_CUSTOM + return (AG(mm_heap)->use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) != 0; +#else + return false; #endif } @@ -2996,6 +3075,7 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap, { #if ZEND_MM_CUSTOM zend_mm_heap *_heap = (zend_mm_heap*)heap; + int observed = _heap->use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED; if (!_malloc && !_free && !_realloc) { _heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_NONE; @@ -3005,6 +3085,8 @@ ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap, _heap->custom_heap.std._free = _free; _heap->custom_heap.std._realloc = _realloc; } + + _heap->use_custom_heap |= observed; #endif } @@ -3016,7 +3098,7 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, #if ZEND_MM_CUSTOM zend_mm_heap *_heap = (zend_mm_heap*)heap; - if (heap->use_custom_heap) { + if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { *_malloc = _heap->custom_heap.std._malloc; *_free = _heap->custom_heap.std._free; *_realloc = _heap->custom_heap.std._realloc; @@ -3032,6 +3114,207 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, #endif } +/* + * Use this function to register observers to the ZendMM. Make sure to call this + * function only during PHP module startup phase and not later. Observers will + * not be called directly after registering them, but only after + * `zend_mm_observers_startup()` was run, which will take place after the PHP + * module startup phase. + */ +ZEND_API zend_mm_observer* zend_mm_observer_register( + void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +) { + ZEND_ASSERT(php_during_module_startup()); +#if ZEND_MM_CUSTOM + zend_mm_observer *node = pemalloc(sizeof(zend_mm_observer), 1); + node->malloc = malloc; + node->free = free; + node->realloc = realloc; + node->next = NULL; + if (zend_mm_observers == NULL) { + zend_mm_observers = node; + return zend_mm_observers; + } + zend_mm_observer *current = zend_mm_observers; + while (current->next != NULL) { + current = current->next; + } + current->next = node; + return node; +#else + return NULL; +#endif +} + +/* + * This function will activate the global registered observers for the current + * heap. After this has been run, observering functions will be called. + */ +void zend_mm_observers_startup(zend_mm_heap *heap) +{ +#if ZEND_MM_CUSTOM + if (heap == NULL) { + heap = AG(mm_heap); + } + zend_mm_observer *current_observer = zend_mm_observers; + if (current_observer == NULL) { + // no observers installed + return; + } + // copy observers into heap + zend_mm_heap_observer *node = NULL; + zend_mm_heap_observer *current_heap_observer = NULL; + while (current_observer != NULL) { + node = pemalloc(sizeof(zend_mm_heap_observer), 1); + node->observer = current_observer; + node->enabled = true; + node->next = NULL; + if (current_heap_observer == NULL) { + heap->observers = node; + } else { + current_heap_observer->next = node; + } + current_heap_observer = node; + current_observer = current_observer->next; + } + // set bitflag for observers being around + heap->use_custom_heap |= ZEND_MM_CUSTOM_HEAP_OBSERVED; +#endif +} + +/* + * This function will shutdown the observers for the current heap and free + * global structures. This will be called before PHP module shutdown phase. + */ +void zend_mm_observers_shutdown(zend_mm_heap *heap) +{ +#if ZEND_MM_CUSTOM + if (heap == NULL) { + heap = AG(mm_heap); + if (heap == NULL) { + return; + } + } + zend_mm_heap_observer *current_heap_observer = heap->observers; + zend_mm_heap_observer *next_heap_observer = NULL; + while (current_heap_observer != NULL) { + next_heap_observer = current_heap_observer->next; + pefree(current_heap_observer, 1); + current_heap_observer = next_heap_observer; + } + heap->observers = NULL; + return; +#endif +} + +void zend_mm_observers_unregister(void) +{ +#if ZEND_MM_CUSTOM + zend_mm_observer *current = zend_mm_observers; + zend_mm_observer *next = NULL; + while (current != NULL) { + next = current->next; + pefree(current, 1); + current = next; + } + zend_mm_observers = NULL; +#endif +} + +/* + * This internal function will check if there are still enabled observer on the + * heap. In case there are, it will set the `ZEND_MM_CUSTOM_HEAP_OBSERVED` flag + * otherwise it will unset the flag. + */ +void zend_mm_observer_sync_flag(bool enabled_observer) { +#if ZEND_MM_CUSTOM + zend_mm_heap *heap = AG(mm_heap); + if (heap == NULL) { + return; + } + if (enabled_observer) { + // When an observer was enabled, we don't need to find if there were + // enabled observers + heap->use_custom_heap |= ZEND_MM_CUSTOM_HEAP_OBSERVED; + return; + } + // An observer got disabled, so we need to check if there are still enabled + // observers left in the list + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->enabled == true) { + // in case we find an enabled observer there is nothing to do + return; + } + current = current->next; + } + heap->use_custom_heap &= ~ZEND_MM_CUSTOM_HEAP_OBSERVED; +#endif +} + +bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { +#if ZEND_MM_CUSTOM + if (heap == NULL) { + heap = AG(mm_heap); + if (heap == NULL) { + return false; + } + } + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->observer == node) { + return current->enabled; + } + current = current->next; + } +#endif + return false; +} + +bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { +#if ZEND_MM_CUSTOM + if (heap == NULL) { + heap = AG(mm_heap); + if (heap == NULL) { + return false; + } + } + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->observer == node) { + current->enabled = true; + zend_mm_observer_sync_flag(true); + return true; + } + current = current->next; + } +#endif + return false; +} + +bool zend_mm_observer_disable(zend_mm_heap *heap, zend_mm_observer *node) { +#if ZEND_MM_CUSTOM + if (heap == NULL) { + heap = AG(mm_heap); + if (heap == NULL) { + return false; + } + } + zend_mm_heap_observer *current = heap->observers; + while (current != NULL) { + if (current->observer == node) { + current->enabled = false; + zend_mm_observer_sync_flag(false); + return true; + } + current = current->next; + } +#endif + return false; +} + #if ZEND_DEBUG ZEND_API void zend_mm_set_custom_debug_handlers(zend_mm_heap *heap, void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), @@ -3041,7 +3324,7 @@ ZEND_API void zend_mm_set_custom_debug_handlers(zend_mm_heap *heap, #if ZEND_MM_CUSTOM zend_mm_heap *_heap = (zend_mm_heap*)heap; - _heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_DEBUG; + _heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_DEBUG | (_heap->use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED); _heap->custom_heap.debug._malloc = _malloc; _heap->custom_heap.debug._free = _free; _heap->custom_heap.debug._realloc = _realloc; diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index eb80bfb14b018..b73a030690a59 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -266,9 +266,10 @@ ZEND_API zend_mm_heap *zend_mm_get_heap(void); ZEND_API size_t zend_mm_gc(zend_mm_heap *heap); -#define ZEND_MM_CUSTOM_HEAP_NONE 0 -#define ZEND_MM_CUSTOM_HEAP_STD 1 -#define ZEND_MM_CUSTOM_HEAP_DEBUG 2 +#define ZEND_MM_CUSTOM_HEAP_NONE 0 +#define ZEND_MM_CUSTOM_HEAP_STD 1 +#define ZEND_MM_CUSTOM_HEAP_DEBUG 2 +#define ZEND_MM_CUSTOM_HEAP_OBSERVED 4 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, @@ -280,6 +281,23 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, void (**_free)(void*), void* (**_realloc)(void*, size_t)); +typedef struct _zend_mm_observer zend_mm_observer; + +// global +ZEND_API zend_mm_observer* zend_mm_observer_register( + void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +); +void zend_mm_observers_startup(zend_mm_heap *heap); +void zend_mm_observers_shutdown(zend_mm_heap *heap); +void zend_mm_observers_unregister(void); + +// thread local +ZEND_API bool zend_mm_observer_enabled(zend_mm_heap*, zend_mm_observer*); +ZEND_API bool zend_mm_observer_enable(zend_mm_heap*, zend_mm_observer*); +ZEND_API bool zend_mm_observer_disable(zend_mm_heap*, zend_mm_observer*); + #if ZEND_DEBUG ZEND_API void zend_mm_set_custom_debug_handlers(zend_mm_heap *heap, void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), diff --git a/main/main.c b/main/main.c index 29d8642b8f713..99ea360319f8a 100644 --- a/main/main.c +++ b/main/main.c @@ -2221,6 +2221,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi php_ini_register_extensions(); zend_startup_modules(); + zend_mm_observers_startup(NULL); + /* start Zend extensions */ zend_startup_extensions(); @@ -2363,6 +2365,10 @@ void php_module_shutdown(void) return; } + // we need to shutdown ZendMM observers before modules are unloaded + zend_mm_observers_shutdown(NULL); + zend_mm_observers_unregister(); + zend_interned_strings_switch_storage(0); #if ZEND_RC_DEBUG From 2932fd6defe3d4aa935b2037853b7ddeafe342c1 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 24 Jul 2023 08:57:33 +0200 Subject: [PATCH 02/26] remove `php.h` --- Zend/zend_alloc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 42f920db62be2..eb771ff1726f9 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -51,7 +51,6 @@ * with more specialized routines when the requested size is known. */ -#include "php.h" #include "zend.h" #include "zend_alloc.h" #include "zend_globals.h" @@ -3126,7 +3125,6 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( void (*free)(void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*realloc)(void *, size_t, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ) { - ZEND_ASSERT(php_during_module_startup()); #if ZEND_MM_CUSTOM zend_mm_observer *node = pemalloc(sizeof(zend_mm_observer), 1); node->malloc = malloc; From 10592030a478dfd842926cec412e97a101b8d7c7 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 24 Jul 2023 09:16:50 +0200 Subject: [PATCH 03/26] Add missing `ZEND_API` --- Zend/zend_alloc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index eb771ff1726f9..094f6ef4e8d19 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -51,6 +51,7 @@ * with more specialized routines when the requested size is known. */ +#include "php.h" #include "zend.h" #include "zend_alloc.h" #include "zend_globals.h" @@ -3252,7 +3253,7 @@ void zend_mm_observer_sync_flag(bool enabled_observer) { #endif } -bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { +ZEND_API bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { #if ZEND_MM_CUSTOM if (heap == NULL) { heap = AG(mm_heap); @@ -3271,7 +3272,7 @@ bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { return false; } -bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { +ZEND_API bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { #if ZEND_MM_CUSTOM if (heap == NULL) { heap = AG(mm_heap); @@ -3292,7 +3293,7 @@ bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { return false; } -bool zend_mm_observer_disable(zend_mm_heap *heap, zend_mm_observer *node) { +ZEND_API bool zend_mm_observer_disable(zend_mm_heap *heap, zend_mm_observer *node) { #if ZEND_MM_CUSTOM if (heap == NULL) { heap = AG(mm_heap); From 267866a230c78d9816316e3bbfb44aed2804dd3c Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 24 Jul 2023 14:30:15 +0200 Subject: [PATCH 04/26] extract `zend_mm_observer_find_heap_observer` --- Zend/zend_alloc.c | 62 +++++++++++++++++++++-------------------------- Zend/zend_alloc.h | 1 + 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 094f6ef4e8d19..03c9b2a7e32b0 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3253,63 +3253,57 @@ void zend_mm_observer_sync_flag(bool enabled_observer) { #endif } -ZEND_API bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { -#if ZEND_MM_CUSTOM +zend_mm_heap_observer* zend_mm_observer_find_heap_observer(zend_mm_heap *heap, zend_mm_observer *node) { if (heap == NULL) { heap = AG(mm_heap); if (heap == NULL) { - return false; + return NULL; } } zend_mm_heap_observer *current = heap->observers; while (current != NULL) { if (current->observer == node) { - return current->enabled; + return current; } current = current->next; } + return NULL; +} + +ZEND_API bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { +#if ZEND_MM_CUSTOM + zend_mm_heap_observer *heap_observer = zend_mm_observer_find_heap_observer(heap, node); + if (heap_observer == NULL) { + return false; + } + return heap_observer->enabled; #endif return false; } -ZEND_API bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { +ZEND_API bool zend_mm_observer_set_state(zend_mm_heap *heap, zend_mm_observer *node, bool state) { #if ZEND_MM_CUSTOM - if (heap == NULL) { - heap = AG(mm_heap); - if (heap == NULL) { - return false; - } - } - zend_mm_heap_observer *current = heap->observers; - while (current != NULL) { - if (current->observer == node) { - current->enabled = true; - zend_mm_observer_sync_flag(true); - return true; - } - current = current->next; + zend_mm_heap_observer *heap_observer = zend_mm_observer_find_heap_observer(heap, node); + if (heap_observer == NULL) { + false; } + heap_observer->enabled = state; + zend_mm_observer_sync_flag(state); + return true; +#endif + return false; +} + +ZEND_API bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { +#if ZEND_MM_CUSTOM + return zend_mm_observer_set_state(heap, node, true); #endif return false; } ZEND_API bool zend_mm_observer_disable(zend_mm_heap *heap, zend_mm_observer *node) { #if ZEND_MM_CUSTOM - if (heap == NULL) { - heap = AG(mm_heap); - if (heap == NULL) { - return false; - } - } - zend_mm_heap_observer *current = heap->observers; - while (current != NULL) { - if (current->observer == node) { - current->enabled = false; - zend_mm_observer_sync_flag(false); - return true; - } - current = current->next; - } + return zend_mm_observer_set_state(heap, node, false); #endif return false; } diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index b73a030690a59..d798a0f687e05 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -295,6 +295,7 @@ void zend_mm_observers_unregister(void); // thread local ZEND_API bool zend_mm_observer_enabled(zend_mm_heap*, zend_mm_observer*); +ZEND_API bool zend_mm_observer_set_state(zend_mm_heap *heap, zend_mm_observer *node, bool state); ZEND_API bool zend_mm_observer_enable(zend_mm_heap*, zend_mm_observer*); ZEND_API bool zend_mm_observer_disable(zend_mm_heap*, zend_mm_observer*); From 793003f33c968032d95128c68dd106b054a34502 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 25 Jul 2023 12:27:29 +0200 Subject: [PATCH 05/26] make observer less complex --- Zend/zend_alloc.c | 252 ++++++++++++++-------------------------------- Zend/zend_alloc.h | 13 +-- main/main.c | 3 - 3 files changed, 81 insertions(+), 187 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 03c9b2a7e32b0..49c0f7aa99a02 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -51,7 +51,6 @@ * with more specialized routines when the requested size is known. */ -#include "php.h" #include "zend.h" #include "zend_alloc.h" #include "zend_globals.h" @@ -203,6 +202,13 @@ typedef struct _zend_mm_huge_list zend_mm_huge_list; static bool zend_mm_use_huge_pages = false; +struct _zend_mm_observer { + void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + zend_mm_observer *next; +}; + /* * Memory is retrieved from OS by chunks of fixed size 2MB. * Inside chunk it's managed by pages of fixed size 4096B. @@ -234,23 +240,6 @@ static bool zend_mm_use_huge_pages = false; * (5 bits) bin number (e.g. 0 for sizes 0-2, 1 for 3-4, * 2 for 5-8, 3 for 9-16 etc) see zend_alloc_sizes.h */ - - -struct _zend_mm_observer { - void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); - zend_mm_observer *next; -}; -zend_mm_observer *zend_mm_observers = NULL; - -typedef struct _zend_mm_heap_observer zend_mm_heap_observer; -struct _zend_mm_heap_observer { - zend_mm_observer *observer; - bool enabled; - zend_mm_heap_observer *next; -}; - struct _zend_mm_heap { #if ZEND_MM_CUSTOM int use_custom_heap; /* bitflag */ @@ -298,7 +287,7 @@ struct _zend_mm_heap { } debug; } custom_heap; HashTable *tracked_allocs; - zend_mm_heap_observer *observers; + zend_mm_observer *observers; #endif }; @@ -1966,7 +1955,6 @@ static zend_mm_heap *zend_mm_init(void) #if ZEND_MM_CUSTOM heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_NONE; heap->observers = NULL; - zend_mm_observers_startup(heap); #endif #if ZEND_MM_STORAGE heap->storage = NULL; @@ -2528,10 +2516,10 @@ static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_D ptr = zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { - zend_mm_heap_observer *current = heap->observers; + zend_mm_observer *current = heap->observers; while (current != NULL) { - if (current->enabled && current->observer != NULL && current->observer->malloc != NULL) { - current->observer->malloc(size, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (current->malloc != NULL) { + current->malloc(size, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } current = current->next; } @@ -2545,10 +2533,10 @@ static ZEND_COLD void ZEND_FASTCALL _efree_custom(void *ptr ZEND_FILE_LINE_DC ZE int use_custom_heap = heap->use_custom_heap; if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { - zend_mm_heap_observer *current = heap->observers; + zend_mm_observer *current = heap->observers; while (current != NULL) { - if (current->enabled && current->observer != NULL && current->observer->free != NULL) { - current->observer->free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (current->free != NULL) { + current->free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } current = current->next; } @@ -2578,10 +2566,10 @@ static ZEND_COLD void* ZEND_FASTCALL _realloc_custom(void *ptr, size_t size ZEND new_ptr = zend_mm_realloc_heap(AG(mm_heap), ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { - zend_mm_heap_observer *current = heap->observers; + zend_mm_observer *current = heap->observers; while (current != NULL) { - if (current->enabled && current->observer != NULL && current->observer->realloc != NULL) { - current->observer->realloc(ptr, size, new_ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (current->realloc != NULL) { + current->realloc(ptr, size, new_ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } current = current->next; } @@ -3000,7 +2988,6 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) mm_heap->tracked_allocs = malloc(sizeof(HashTable)); zend_hash_init(mm_heap->tracked_allocs, 1024, NULL, NULL, 1); } - zend_mm_observers_startup(mm_heap); return; } #endif @@ -3059,10 +3046,13 @@ ZEND_API bool zend_mm_is_custom_heap(zend_mm_heap *new_heap) #endif } -ZEND_API bool zend_mm_is_observed(zend_mm_heap *new_heap) +ZEND_API bool zend_mm_is_observed(zend_mm_heap *heap) { #if ZEND_MM_CUSTOM - return (AG(mm_heap)->use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) != 0; + if (!heap && !(heap = AG(mm_heap))) { + return false; + } + return (heap->use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) != 0; #else return false; #endif @@ -3115,32 +3105,40 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, } /* - * Use this function to register observers to the ZendMM. Make sure to call this - * function only during PHP module startup phase and not later. Observers will - * not be called directly after registering them, but only after - * `zend_mm_observers_startup()` was run, which will take place after the PHP - * module startup phase. + * Use this function to register observers to the ZendMM. + * This function operates on the thread local heap and is meant to be called in + * RINIT. Calling it in MINIT works only in NTS builds, but not in ZTS. */ ZEND_API zend_mm_observer* zend_mm_observer_register( + zend_mm_heap* heap, void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ) { #if ZEND_MM_CUSTOM + if (!heap && !(heap = AG(mm_heap))) { + return false; + } + zend_mm_observer *node = pemalloc(sizeof(zend_mm_observer), 1); node->malloc = malloc; node->free = free; node->realloc = realloc; node->next = NULL; - if (zend_mm_observers == NULL) { - zend_mm_observers = node; - return zend_mm_observers; + + // set bitflag for observers being around + heap->use_custom_heap |= ZEND_MM_CUSTOM_HEAP_OBSERVED; + + if (heap->observers == NULL) { + heap->observers = node; + return node; } - zend_mm_observer *current = zend_mm_observers; + zend_mm_observer *current = heap->observers; while (current->next != NULL) { current = current->next; } current->next = node; + return node; #else return NULL; @@ -3148,166 +3146,70 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( } /* - * This function will activate the global registered observers for the current - * heap. After this has been run, observering functions will be called. + * Use this function to unregister your observer at any given time. In case your + * observer was the last one to be unregistered, this will also reset the + * `heap->use_custom_heap` bitflag. */ -void zend_mm_observers_startup(zend_mm_heap *heap) +ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer *observer) { #if ZEND_MM_CUSTOM - if (heap == NULL) { - heap = AG(mm_heap); - } - zend_mm_observer *current_observer = zend_mm_observers; - if (current_observer == NULL) { - // no observers installed - return; + if (!heap && !(heap = AG(mm_heap))) { + return false; } - // copy observers into heap - zend_mm_heap_observer *node = NULL; - zend_mm_heap_observer *current_heap_observer = NULL; - while (current_observer != NULL) { - node = pemalloc(sizeof(zend_mm_heap_observer), 1); - node->observer = current_observer; - node->enabled = true; - node->next = NULL; - if (current_heap_observer == NULL) { - heap->observers = node; - } else { - current_heap_observer->next = node; + + zend_mm_observer *current = heap->observers, *prev = NULL; + + if (current == observer) { + heap->observers = observer->next; + pefree(observer, 1); + if (!heap->observers) + { + // this was the one and only installed observer + heap->use_custom_heap &= ~ZEND_MM_CUSTOM_HEAP_OBSERVED; } - current_heap_observer = node; - current_observer = current_observer->next; + return true; } - // set bitflag for observers being around - heap->use_custom_heap |= ZEND_MM_CUSTOM_HEAP_OBSERVED; + + while (current != NULL && current != observer) { + prev = current; + current = current->next; + } + + // did not find observer or NULL was given + if (current == NULL) + return false; + + prev->next = current->next; + pefree(observer, 1); + return true; +#else + return false; #endif } /* * This function will shutdown the observers for the current heap and free - * global structures. This will be called before PHP module shutdown phase. + * memory */ void zend_mm_observers_shutdown(zend_mm_heap *heap) { #if ZEND_MM_CUSTOM - if (heap == NULL) { - heap = AG(mm_heap); - if (heap == NULL) { - return; - } - } - zend_mm_heap_observer *current_heap_observer = heap->observers; - zend_mm_heap_observer *next_heap_observer = NULL; - while (current_heap_observer != NULL) { - next_heap_observer = current_heap_observer->next; - pefree(current_heap_observer, 1); - current_heap_observer = next_heap_observer; + if (!heap && !(heap = AG(mm_heap))) { + return; } - heap->observers = NULL; - return; -#endif -} - -void zend_mm_observers_unregister(void) -{ -#if ZEND_MM_CUSTOM - zend_mm_observer *current = zend_mm_observers; + zend_mm_observer *current = heap->observers; zend_mm_observer *next = NULL; while (current != NULL) { next = current->next; pefree(current, 1); current = next; } - zend_mm_observers = NULL; -#endif -} - -/* - * This internal function will check if there are still enabled observer on the - * heap. In case there are, it will set the `ZEND_MM_CUSTOM_HEAP_OBSERVED` flag - * otherwise it will unset the flag. - */ -void zend_mm_observer_sync_flag(bool enabled_observer) { -#if ZEND_MM_CUSTOM - zend_mm_heap *heap = AG(mm_heap); - if (heap == NULL) { - return; - } - if (enabled_observer) { - // When an observer was enabled, we don't need to find if there were - // enabled observers - heap->use_custom_heap |= ZEND_MM_CUSTOM_HEAP_OBSERVED; - return; - } - // An observer got disabled, so we need to check if there are still enabled - // observers left in the list - zend_mm_heap_observer *current = heap->observers; - while (current != NULL) { - if (current->enabled == true) { - // in case we find an enabled observer there is nothing to do - return; - } - current = current->next; - } + heap->observers = NULL; heap->use_custom_heap &= ~ZEND_MM_CUSTOM_HEAP_OBSERVED; + return; #endif } -zend_mm_heap_observer* zend_mm_observer_find_heap_observer(zend_mm_heap *heap, zend_mm_observer *node) { - if (heap == NULL) { - heap = AG(mm_heap); - if (heap == NULL) { - return NULL; - } - } - zend_mm_heap_observer *current = heap->observers; - while (current != NULL) { - if (current->observer == node) { - return current; - } - current = current->next; - } - return NULL; -} - -ZEND_API bool zend_mm_observer_enabled(zend_mm_heap *heap, zend_mm_observer *node) { -#if ZEND_MM_CUSTOM - zend_mm_heap_observer *heap_observer = zend_mm_observer_find_heap_observer(heap, node); - if (heap_observer == NULL) { - return false; - } - return heap_observer->enabled; -#endif - return false; -} - -ZEND_API bool zend_mm_observer_set_state(zend_mm_heap *heap, zend_mm_observer *node, bool state) { -#if ZEND_MM_CUSTOM - zend_mm_heap_observer *heap_observer = zend_mm_observer_find_heap_observer(heap, node); - if (heap_observer == NULL) { - false; - } - heap_observer->enabled = state; - zend_mm_observer_sync_flag(state); - return true; -#endif - return false; -} - -ZEND_API bool zend_mm_observer_enable(zend_mm_heap *heap, zend_mm_observer *node) { -#if ZEND_MM_CUSTOM - return zend_mm_observer_set_state(heap, node, true); -#endif - return false; -} - -ZEND_API bool zend_mm_observer_disable(zend_mm_heap *heap, zend_mm_observer *node) { -#if ZEND_MM_CUSTOM - return zend_mm_observer_set_state(heap, node, false); -#endif - return false; -} - #if ZEND_DEBUG ZEND_API void zend_mm_set_custom_debug_handlers(zend_mm_heap *heap, void* (*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index d798a0f687e05..e61f498ad2c9c 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -283,21 +283,16 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, typedef struct _zend_mm_observer zend_mm_observer; -// global +// thread local +ZEND_API bool zend_mm_is_observed(zend_mm_heap *heap); ZEND_API zend_mm_observer* zend_mm_observer_register( + zend_mm_heap *heap, void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ); -void zend_mm_observers_startup(zend_mm_heap *heap); +ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer *observer); void zend_mm_observers_shutdown(zend_mm_heap *heap); -void zend_mm_observers_unregister(void); - -// thread local -ZEND_API bool zend_mm_observer_enabled(zend_mm_heap*, zend_mm_observer*); -ZEND_API bool zend_mm_observer_set_state(zend_mm_heap *heap, zend_mm_observer *node, bool state); -ZEND_API bool zend_mm_observer_enable(zend_mm_heap*, zend_mm_observer*); -ZEND_API bool zend_mm_observer_disable(zend_mm_heap*, zend_mm_observer*); #if ZEND_DEBUG ZEND_API void zend_mm_set_custom_debug_handlers(zend_mm_heap *heap, diff --git a/main/main.c b/main/main.c index 99ea360319f8a..32ff288b7a63c 100644 --- a/main/main.c +++ b/main/main.c @@ -2221,8 +2221,6 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi php_ini_register_extensions(); zend_startup_modules(); - zend_mm_observers_startup(NULL); - /* start Zend extensions */ zend_startup_extensions(); @@ -2367,7 +2365,6 @@ void php_module_shutdown(void) // we need to shutdown ZendMM observers before modules are unloaded zend_mm_observers_shutdown(NULL); - zend_mm_observers_unregister(); zend_interned_strings_switch_storage(0); From f8b0601e2e128a788d62b21a0d542912627941f2 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 25 Jul 2023 21:07:47 +0200 Subject: [PATCH 06/26] fix comments and code style --- Zend/zend_alloc.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 49c0f7aa99a02..26f7856e7edbf 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3106,8 +3106,9 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, /* * Use this function to register observers to the ZendMM. - * This function operates on the thread local heap and is meant to be called in - * RINIT. Calling it in MINIT works only in NTS builds, but not in ZTS. + * This function operates on the thread local heap and is meant to be called + * during the request lifetime. Calling it in MINIT works only in NTS builds, + * but not in ZTS. */ ZEND_API zend_mm_observer* zend_mm_observer_register( zend_mm_heap* heap, @@ -3116,9 +3117,10 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( void (*realloc)(void *, size_t, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ) { #if ZEND_MM_CUSTOM - if (!heap && !(heap = AG(mm_heap))) { - return false; + if (!heap) { + heap = AG(mm_heap); } + ZEND_ASSERT(heap); zend_mm_observer *node = pemalloc(sizeof(zend_mm_observer), 1); node->malloc = malloc; @@ -3153,17 +3155,17 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer *observer) { #if ZEND_MM_CUSTOM - if (!heap && !(heap = AG(mm_heap))) { - return false; + if (!heap) { + heap = AG(mm_heap); } + ZEND_ASSERT(heap); zend_mm_observer *current = heap->observers, *prev = NULL; if (current == observer) { heap->observers = observer->next; pefree(observer, 1); - if (!heap->observers) - { + if (!heap->observers) { // this was the one and only installed observer heap->use_custom_heap &= ~ZEND_MM_CUSTOM_HEAP_OBSERVED; } @@ -3176,8 +3178,9 @@ ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer * } // did not find observer or NULL was given - if (current == NULL) + if (current == NULL) { return false; + } prev->next = current->next; pefree(observer, 1); @@ -3194,9 +3197,10 @@ ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer * void zend_mm_observers_shutdown(zend_mm_heap *heap) { #if ZEND_MM_CUSTOM - if (!heap && !(heap = AG(mm_heap))) { - return; + if (!heap) { + heap = AG(mm_heap); } + ZEND_ASSERT(heap); zend_mm_observer *current = heap->observers; zend_mm_observer *next = NULL; while (current != NULL) { From 0759afcb36a02ec77f12b3fc82b2d372d8115853 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 25 Jul 2023 21:28:41 +0200 Subject: [PATCH 07/26] shutdown ZendMM observers during request shutdown --- Zend/zend_alloc.c | 4 ++++ main/main.c | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 26f7856e7edbf..d3d384ec71e37 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2278,6 +2278,10 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) zend_mm_chunk *p; zend_mm_huge_list *list; + if (full == false) { + zend_mm_observers_shutdown(heap); + } + #if ZEND_MM_CUSTOM if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { if (heap->custom_heap.std._malloc == tracked_malloc) { diff --git a/main/main.c b/main/main.c index 32ff288b7a63c..29d8642b8f713 100644 --- a/main/main.c +++ b/main/main.c @@ -2363,9 +2363,6 @@ void php_module_shutdown(void) return; } - // we need to shutdown ZendMM observers before modules are unloaded - zend_mm_observers_shutdown(NULL); - zend_interned_strings_switch_storage(0); #if ZEND_RC_DEBUG From 55b81abe985ab7ceb4d1e21636e72016ea3f2e83 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 26 Jul 2023 12:24:41 +0200 Subject: [PATCH 08/26] small cleanup --- Zend/zend_alloc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index d3d384ec71e37..0e2a21d0c0e48 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2278,11 +2278,8 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) zend_mm_chunk *p; zend_mm_huge_list *list; - if (full == false) { - zend_mm_observers_shutdown(heap); - } - #if ZEND_MM_CUSTOM + zend_mm_observers_shutdown(heap); if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { if (heap->custom_heap.std._malloc == tracked_malloc) { if (silent) { @@ -3006,7 +3003,6 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) #ifdef ZTS static void alloc_globals_dtor(zend_alloc_globals *alloc_globals) { - zend_mm_observers_shutdown(alloc_globals->mm_heap); zend_mm_shutdown(alloc_globals->mm_heap, 1, 1); } #endif @@ -3196,7 +3192,7 @@ ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer * /* * This function will shutdown the observers for the current heap and free - * memory + * memory, it is called in `zend_mm_shutdown()` */ void zend_mm_observers_shutdown(zend_mm_heap *heap) { From fecacac5df615be01efcfe0fd926e68a54827152 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Thu, 27 Jul 2023 16:17:31 +0200 Subject: [PATCH 09/26] cleanup unregister --- Zend/zend_alloc.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 0e2a21d0c0e48..049aa27d9c243 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3162,16 +3162,7 @@ ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer * zend_mm_observer *current = heap->observers, *prev = NULL; - if (current == observer) { - heap->observers = observer->next; - pefree(observer, 1); - if (!heap->observers) { - // this was the one and only installed observer - heap->use_custom_heap &= ~ZEND_MM_CUSTOM_HEAP_OBSERVED; - } - return true; - } - + // find the given observer in the list of observers while (current != NULL && current != observer) { prev = current; current = current->next; @@ -3182,7 +3173,18 @@ ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer * return false; } - prev->next = current->next; + if (prev == NULL) { + // current observer is the first in the list + heap->observers = current->next; + if (!heap->observers) { + // and current was also the last in the list, so no more observers + // left + heap->use_custom_heap &= ~ZEND_MM_CUSTOM_HEAP_OBSERVED; + } + } else { + prev->next = current->next; + } + pefree(observer, 1); return true; #else From 88fbb2cb0458182849418735127583354e13a53b Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 26 Jul 2023 12:53:16 +0200 Subject: [PATCH 10/26] add tests for ZendMM Observer --- ext/zend_test/config.m4 | 2 +- ext/zend_test/config.w32 | 2 +- ext/zend_test/php_test.h | 3 + ext/zend_test/test.c | 4 + .../tests/zendmm_observer_all_01.phpt | 16 +++ .../tests/zendmm_observer_part_01.phpt | 24 ++++ ext/zend_test/zendmm_observer.c | 117 ++++++++++++++++++ ext/zend_test/zendmm_observer.h | 24 ++++ ext/zend_test/zendmm_observer.stub.php | 9 ++ ext/zend_test/zendmm_observer_arginfo.h | 18 +++ 10 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 ext/zend_test/tests/zendmm_observer_all_01.phpt create mode 100644 ext/zend_test/tests/zendmm_observer_part_01.phpt create mode 100644 ext/zend_test/zendmm_observer.c create mode 100644 ext/zend_test/zendmm_observer.h create mode 100644 ext/zend_test/zendmm_observer.stub.php create mode 100644 ext/zend_test/zendmm_observer_arginfo.h diff --git a/ext/zend_test/config.m4 b/ext/zend_test/config.m4 index 4b15db47750cb..8ac0704947fdb 100644 --- a/ext/zend_test/config.m4 +++ b/ext/zend_test/config.m4 @@ -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 zendmm_observer.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi diff --git a/ext/zend_test/config.w32 b/ext/zend_test/config.w32 index e066fdeb243ea..f855818c4b72a 100644 --- a/ext/zend_test/config.w32 +++ b/ext/zend_test/config.w32 @@ -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 zendmm_observer.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS "); } diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index 89570d1155cea..004f131b24795 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -18,6 +18,7 @@ #define PHP_TEST_H #include "fiber.h" +#include "Zend/zend_alloc.h" extern zend_module_entry zend_test_module_entry; #define phpext_zend_test_ptr &zend_test_module_entry @@ -62,6 +63,8 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) zend_long quantity_value; zend_string *str_test; zend_string *not_empty_str_test; + int zendmm_observer_enabled; + zend_mm_observer *observer; ZEND_END_MODULE_GLOBALS(zend_test) extern ZEND_DECLARE_MODULE_GLOBALS(zend_test) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index ba91328596926..ba0a518b1dbd9 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -23,6 +23,7 @@ #include "ext/standard/info.h" #include "php_test.h" #include "observer.h" +#include "zendmm_observer.h" #include "fiber.h" #include "iterators.h" #include "object_handlers.h" @@ -1189,6 +1190,7 @@ PHP_MINIT_FUNCTION(zend_test) } zend_test_observer_init(INIT_FUNC_ARGS_PASSTHRU); + zend_test_mm_observer_minit(INIT_FUNC_ARGS_PASSTHRU); zend_test_fiber_init(); zend_test_iterators_init(); zend_test_object_handlers_init(); @@ -1217,6 +1219,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_observer_rinit(); return SUCCESS; } @@ -1234,6 +1237,7 @@ PHP_RSHUTDOWN_FUNCTION(zend_test) zend_mm_set_heap(ZT_G(zend_orig_heap)); } + zend_test_mm_observer_rshutdown(); return SUCCESS; } diff --git a/ext/zend_test/tests/zendmm_observer_all_01.phpt b/ext/zend_test/tests/zendmm_observer_all_01.phpt new file mode 100644 index 0000000000000..6ac0cdb4d05c0 --- /dev/null +++ b/ext/zend_test/tests/zendmm_observer_all_01.phpt @@ -0,0 +1,16 @@ +--TEST-- +ZendMM Observer: Observe all +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zendmm_observer.enabled=1 +opcache.enable=0 +--FILE-- + +--EXPECTREGEX-- +.* +ZendMM Observer enabled +.* +.* +ZendMM Observer disabled diff --git a/ext/zend_test/tests/zendmm_observer_part_01.phpt b/ext/zend_test/tests/zendmm_observer_part_01.phpt new file mode 100644 index 0000000000000..b5671206bc03e --- /dev/null +++ b/ext/zend_test/tests/zendmm_observer_part_01.phpt @@ -0,0 +1,24 @@ +--TEST-- +ZendMM Observer: Observe all +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zendmm_observer.enabled=0 +opcache.enable=0 +--FILE-- + +--EXPECTF-- +malloc 0x%s of size %d (block: %d) +malloc 0x%s of size %d (block: %d) +freed 0x%s of size %d diff --git a/ext/zend_test/zendmm_observer.c b/ext/zend_test/zendmm_observer.c new file mode 100644 index 0000000000000..fa42e91f934c8 --- /dev/null +++ b/ext/zend_test/zendmm_observer.c @@ -0,0 +1,117 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_test.h" +#include "Zend/zend_alloc.h" +#include "ext/standard/info.h" +#include "ext/standard/php_var.h" +#include "zendmm_observer.h" + +void observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + size_t block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + printf("malloc %p of size %zu (block: %zu)\n", ptr, len, block_len); +} + +void observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + size_t block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + printf("freed %p of size %zu\n", ptr, block_len); +} + +void observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + size_t block_len = zend_mm_block_size(zend_mm_get_heap(), newptr); + printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); +} + +PHP_FUNCTION(memprof_enable) +{ + ZEND_PARSE_PARAMETERS_NONE(); + if (ZT_G(observer)) { + RETURN_FALSE; + } + ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); + RETURN_TRUE; +} + +PHP_FUNCTION(memprof_disable) +{ + ZEND_PARSE_PARAMETERS_NONE(); + zend_mm_observer_unregister(zend_mm_get_heap(), ZT_G(observer)); + ZT_G(observer) = NULL; + RETURN_TRUE; +} + +static PHP_INI_MH(OnUpdate_zend_test_zendmm_observer_enabled) +{ + int int_value; + if (new_value == NULL) { + return FAILURE; + } + + if (zend_string_equals_literal_ci(new_value, "true")) { + int_value = 1; + } else if (zend_string_equals_literal_ci(new_value, "false")) { + int_value = 0; + } else { + int_value = (int) zend_ini_parse_quantity_warn(new_value, entry->name); + } + + if (int_value == 1) { + if (ZT_G(observer) == NULL) { + ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); + } + } else { + if (ZT_G(observer) != NULL) { + zend_mm_observer_unregister(zend_mm_get_heap(), ZT_G(observer)); + ZT_G(observer) = NULL; + } + } + return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); +} + +PHP_INI_BEGIN() + STD_PHP_INI_BOOLEAN("zend_test.zendmm_observer.enabled", "0", PHP_INI_ALL, OnUpdate_zend_test_zendmm_observer_enabled, zendmm_observer_enabled, zend_zend_test_globals, zend_test_globals) +PHP_INI_END() + +void zend_test_mm_observer_minit(INIT_FUNC_ARGS) +{ + if (type != MODULE_TEMPORARY) { + REGISTER_INI_ENTRIES(); + } else { + (void)ini_entries; + } +} + +void zend_test_mm_observer_rinit(void) +{ + if (ZT_G(zendmm_observer_enabled)) { + printf("ZendMM Observer enabled\n"); + ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); + } +} + +void zend_test_mm_observer_rshutdown(void) +{ + if (ZT_G(observer)) { + printf("ZendMM Observer disabled\n"); + zend_mm_observer_unregister(zend_mm_get_heap(), ZT_G(observer)); + } + ZT_G(observer) = NULL; +} + diff --git a/ext/zend_test/zendmm_observer.h b/ext/zend_test/zendmm_observer.h new file mode 100644 index 0000000000000..c58a452516b1b --- /dev/null +++ b/ext/zend_test/zendmm_observer.h @@ -0,0 +1,24 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_TEST_MM_OBSERVER_H +#define ZEND_TEST_MM_OBSERVER_H + +void zend_test_mm_observer_minit(INIT_FUNC_ARGS); +void zend_test_mm_observer_rinit(void); +void zend_test_mm_observer_rshutdown(void); + +#endif diff --git a/ext/zend_test/zendmm_observer.stub.php b/ext/zend_test/zendmm_observer.stub.php new file mode 100644 index 0000000000000..9d90af1a784b1 --- /dev/null +++ b/ext/zend_test/zendmm_observer.stub.php @@ -0,0 +1,9 @@ + Date: Fri, 28 Jul 2023 15:12:23 +0200 Subject: [PATCH 11/26] rename zendmm -> zend_mm --- ext/zend_test/config.m4 | 2 +- ext/zend_test/config.w32 | 2 +- ext/zend_test/php_test.h | 2 +- ext/zend_test/test.c | 2 +- ...server_all_01.phpt => zend_mm_observer_all_01.phpt} | 2 +- ...rver_part_01.phpt => zend_mm_observer_part_01.phpt} | 10 +++++----- .../{zendmm_observer.c => zend_mm_observer.c} | 8 ++++---- .../{zendmm_observer.h => zend_mm_observer.h} | 0 ...dmm_observer.stub.php => zend_mm_observer.stub.php} | 0 ...m_observer_arginfo.h => zend_mm_observer_arginfo.h} | 0 10 files changed, 14 insertions(+), 14 deletions(-) rename ext/zend_test/tests/{zendmm_observer_all_01.phpt => zend_mm_observer_all_01.phpt} (82%) rename ext/zend_test/tests/{zendmm_observer_part_01.phpt => zend_mm_observer_part_01.phpt} (58%) rename ext/zend_test/{zendmm_observer.c => zend_mm_observer.c} (91%) rename ext/zend_test/{zendmm_observer.h => zend_mm_observer.h} (100%) rename ext/zend_test/{zendmm_observer.stub.php => zend_mm_observer.stub.php} (100%) rename ext/zend_test/{zendmm_observer_arginfo.h => zend_mm_observer_arginfo.h} (100%) diff --git a/ext/zend_test/config.m4 b/ext/zend_test/config.m4 index 8ac0704947fdb..6bef2b0577d1d 100644 --- a/ext/zend_test/config.m4 +++ b/ext/zend_test/config.m4 @@ -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 zendmm_observer.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_observer.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) fi diff --git a/ext/zend_test/config.w32 b/ext/zend_test/config.w32 index f855818c4b72a..565df8023c8fc 100644 --- a/ext/zend_test/config.w32 +++ b/ext/zend_test/config.w32 @@ -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 zendmm_observer.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_observer.c", PHP_ZEND_TEST_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); ADD_FLAG("CFLAGS_ZEND_TEST", "/D PHP_ZEND_TEST_EXPORTS "); } diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index 004f131b24795..14f60cbbe7605 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -63,7 +63,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) zend_long quantity_value; zend_string *str_test; zend_string *not_empty_str_test; - int zendmm_observer_enabled; + int zend_mm_observer_enabled; zend_mm_observer *observer; ZEND_END_MODULE_GLOBALS(zend_test) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index ba0a518b1dbd9..fa943b0ed8e4d 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -23,7 +23,7 @@ #include "ext/standard/info.h" #include "php_test.h" #include "observer.h" -#include "zendmm_observer.h" +#include "zend_mm_observer.h" #include "fiber.h" #include "iterators.h" #include "object_handlers.h" diff --git a/ext/zend_test/tests/zendmm_observer_all_01.phpt b/ext/zend_test/tests/zend_mm_observer_all_01.phpt similarity index 82% rename from ext/zend_test/tests/zendmm_observer_all_01.phpt rename to ext/zend_test/tests/zend_mm_observer_all_01.phpt index 6ac0cdb4d05c0..76d5fc61ca1d3 100644 --- a/ext/zend_test/tests/zendmm_observer_all_01.phpt +++ b/ext/zend_test/tests/zend_mm_observer_all_01.phpt @@ -3,7 +3,7 @@ ZendMM Observer: Observe all --EXTENSIONS-- zend_test --INI-- -zend_test.zendmm_observer.enabled=1 +zend_test.zend_mm_observer.enabled=1 opcache.enable=0 --FILE-- --EXPECTF-- malloc 0x%s of size %d (block: %d) diff --git a/ext/zend_test/zendmm_observer.c b/ext/zend_test/zend_mm_observer.c similarity index 91% rename from ext/zend_test/zendmm_observer.c rename to ext/zend_test/zend_mm_observer.c index fa42e91f934c8..6e8b854dc2146 100644 --- a/ext/zend_test/zendmm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -19,7 +19,7 @@ #include "Zend/zend_alloc.h" #include "ext/standard/info.h" #include "ext/standard/php_var.h" -#include "zendmm_observer.h" +#include "zend_mm_observer.h" void observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { @@ -57,7 +57,7 @@ PHP_FUNCTION(memprof_disable) RETURN_TRUE; } -static PHP_INI_MH(OnUpdate_zend_test_zendmm_observer_enabled) +static PHP_INI_MH(OnUpdate_zend_test_zend_mm_observer_enabled) { int int_value; if (new_value == NULL) { @@ -86,7 +86,7 @@ static PHP_INI_MH(OnUpdate_zend_test_zendmm_observer_enabled) } PHP_INI_BEGIN() - STD_PHP_INI_BOOLEAN("zend_test.zendmm_observer.enabled", "0", PHP_INI_ALL, OnUpdate_zend_test_zendmm_observer_enabled, zendmm_observer_enabled, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.zend_mm_observer.enabled", "0", PHP_INI_ALL, OnUpdate_zend_test_zend_mm_observer_enabled, zend_mm_observer_enabled, zend_zend_test_globals, zend_test_globals) PHP_INI_END() void zend_test_mm_observer_minit(INIT_FUNC_ARGS) @@ -100,7 +100,7 @@ void zend_test_mm_observer_minit(INIT_FUNC_ARGS) void zend_test_mm_observer_rinit(void) { - if (ZT_G(zendmm_observer_enabled)) { + if (ZT_G(zend_mm_observer_enabled)) { printf("ZendMM Observer enabled\n"); ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); } diff --git a/ext/zend_test/zendmm_observer.h b/ext/zend_test/zend_mm_observer.h similarity index 100% rename from ext/zend_test/zendmm_observer.h rename to ext/zend_test/zend_mm_observer.h diff --git a/ext/zend_test/zendmm_observer.stub.php b/ext/zend_test/zend_mm_observer.stub.php similarity index 100% rename from ext/zend_test/zendmm_observer.stub.php rename to ext/zend_test/zend_mm_observer.stub.php diff --git a/ext/zend_test/zendmm_observer_arginfo.h b/ext/zend_test/zend_mm_observer_arginfo.h similarity index 100% rename from ext/zend_test/zendmm_observer_arginfo.h rename to ext/zend_test/zend_mm_observer_arginfo.h From c74e50ac63bf1027b096145f1c064307082efce5 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 28 Jul 2023 15:18:19 +0200 Subject: [PATCH 12/26] removed unused functions --- ext/zend_test/zend_mm_observer.c | 18 ------------------ ext/zend_test/zend_mm_observer.stub.php | 9 --------- ext/zend_test/zend_mm_observer_arginfo.h | 18 ------------------ 3 files changed, 45 deletions(-) delete mode 100644 ext/zend_test/zend_mm_observer.stub.php delete mode 100644 ext/zend_test/zend_mm_observer_arginfo.h diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 6e8b854dc2146..d81a7af079715 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -39,24 +39,6 @@ void observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); } -PHP_FUNCTION(memprof_enable) -{ - ZEND_PARSE_PARAMETERS_NONE(); - if (ZT_G(observer)) { - RETURN_FALSE; - } - ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); - RETURN_TRUE; -} - -PHP_FUNCTION(memprof_disable) -{ - ZEND_PARSE_PARAMETERS_NONE(); - zend_mm_observer_unregister(zend_mm_get_heap(), ZT_G(observer)); - ZT_G(observer) = NULL; - RETURN_TRUE; -} - static PHP_INI_MH(OnUpdate_zend_test_zend_mm_observer_enabled) { int int_value; diff --git a/ext/zend_test/zend_mm_observer.stub.php b/ext/zend_test/zend_mm_observer.stub.php deleted file mode 100644 index 9d90af1a784b1..0000000000000 --- a/ext/zend_test/zend_mm_observer.stub.php +++ /dev/null @@ -1,9 +0,0 @@ - Date: Fri, 28 Jul 2023 15:21:14 +0200 Subject: [PATCH 13/26] add missing author --- ext/zend_test/zend_mm_observer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zend_test/zend_mm_observer.h b/ext/zend_test/zend_mm_observer.h index c58a452516b1b..1276d6087121f 100644 --- a/ext/zend_test/zend_mm_observer.h +++ b/ext/zend_test/zend_mm_observer.h @@ -10,7 +10,7 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: | + | Author: Florian Engelhardt | +----------------------------------------------------------------------+ */ From 3167fffbda686ea8fea9204db5855fcf488a0ed2 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 28 Jul 2023 15:22:28 +0200 Subject: [PATCH 14/26] remove bad practice comment --- Zend/zend_alloc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 049aa27d9c243..ed83bcaefebd3 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -3107,8 +3107,7 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, /* * Use this function to register observers to the ZendMM. * This function operates on the thread local heap and is meant to be called - * during the request lifetime. Calling it in MINIT works only in NTS builds, - * but not in ZTS. + * during the request lifetime. */ ZEND_API zend_mm_observer* zend_mm_observer_register( zend_mm_heap* heap, From d11819affecb268699eb842d466b4cdb7571e1c9 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 28 Jul 2023 15:25:55 +0200 Subject: [PATCH 15/26] fix naming and bool parsing --- ext/zend_test/zend_mm_observer.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index d81a7af079715..5c214ddc74752 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -39,20 +39,13 @@ void observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); } -static PHP_INI_MH(OnUpdate_zend_test_zend_mm_observer_enabled) +static PHP_INI_MH(OnUpdateZendTestMMObserverEnabled) { - int int_value; if (new_value == NULL) { return FAILURE; } - if (zend_string_equals_literal_ci(new_value, "true")) { - int_value = 1; - } else if (zend_string_equals_literal_ci(new_value, "false")) { - int_value = 0; - } else { - int_value = (int) zend_ini_parse_quantity_warn(new_value, entry->name); - } + int int_value = zend_ini_parse_bool(new_value); if (int_value == 1) { if (ZT_G(observer) == NULL) { @@ -68,7 +61,7 @@ static PHP_INI_MH(OnUpdate_zend_test_zend_mm_observer_enabled) } PHP_INI_BEGIN() - STD_PHP_INI_BOOLEAN("zend_test.zend_mm_observer.enabled", "0", PHP_INI_ALL, OnUpdate_zend_test_zend_mm_observer_enabled, zend_mm_observer_enabled, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.zend_mm_observer.enabled", "0", PHP_INI_ALL, OnUpdateZendTestMMObserverEnabled, zend_mm_observer_enabled, zend_zend_test_globals, zend_test_globals) PHP_INI_END() void zend_test_mm_observer_minit(INIT_FUNC_ARGS) From a7ea2883b9d0e4c4649b4733c5ac0aebbe491981 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 28 Jul 2023 15:32:06 +0200 Subject: [PATCH 16/26] make functions static to avoid name clashes --- ext/zend_test/zend_mm_observer.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 5c214ddc74752..15c79f9ac9f89 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -21,21 +21,30 @@ #include "ext/standard/php_var.h" #include "zend_mm_observer.h" -void observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - size_t block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + size_t block_len = 0; + if (is_zend_ptr(ptr)) { + block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + } printf("malloc %p of size %zu (block: %zu)\n", ptr, len, block_len); } -void observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - size_t block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + size_t block_len = 0; + if (is_zend_ptr(ptr)) { + block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + } printf("freed %p of size %zu\n", ptr, block_len); } -void observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +static void zend_mm_test_observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - size_t block_len = zend_mm_block_size(zend_mm_get_heap(), newptr); + size_t block_len = 0; + if (is_zend_ptr(ptr)) { + block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); + } printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); } @@ -49,7 +58,7 @@ static PHP_INI_MH(OnUpdateZendTestMMObserverEnabled) if (int_value == 1) { if (ZT_G(observer) == NULL) { - ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); + ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc); } } else { if (ZT_G(observer) != NULL) { @@ -77,7 +86,7 @@ void zend_test_mm_observer_rinit(void) { if (ZT_G(zend_mm_observer_enabled)) { printf("ZendMM Observer enabled\n"); - ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), observer_malloc, observer_free, observer_realloc); + ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc); } } From 2256cf72f091800918dd55808c12c72c51426116 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 28 Jul 2023 15:32:37 +0200 Subject: [PATCH 17/26] simplyfy test --- ext/zend_test/tests/zend_mm_observer_all_01.phpt | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/zend_test/tests/zend_mm_observer_all_01.phpt b/ext/zend_test/tests/zend_mm_observer_all_01.phpt index 76d5fc61ca1d3..b26eb3d13ca6f 100644 --- a/ext/zend_test/tests/zend_mm_observer_all_01.phpt +++ b/ext/zend_test/tests/zend_mm_observer_all_01.phpt @@ -12,5 +12,4 @@ opcache.enable=0 .* ZendMM Observer enabled .* -.* ZendMM Observer disabled From e5d60ddb2c4d6e811edd1af33a6fc2b737ef2058 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 28 Jul 2023 15:55:36 +0200 Subject: [PATCH 18/26] deduplicate observer calling --- Zend/zend_alloc.c | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index ed83bcaefebd3..3bbb44083d378 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2503,6 +2503,17 @@ ZEND_API bool is_zend_ptr(const void *ptr) #if ZEND_MM_CUSTOM +#define HANDLE_OBSERVERS(observer_function, ...) \ + if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { \ + zend_mm_observer *current = heap->observers; \ + while (current != NULL) { \ + if (current->observer_function != NULL) { \ + current->observer_function(__VA_ARGS__ ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); \ + } \ + current = current->next; \ + } \ + } + static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { void *ptr; @@ -2516,15 +2527,7 @@ static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_D // no custom memory manager, only observer present ptr = zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { - zend_mm_observer *current = heap->observers; - while (current != NULL) { - if (current->malloc != NULL) { - current->malloc(size, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - } - current = current->next; - } - } + HANDLE_OBSERVERS(malloc, size, ptr) return ptr; } @@ -2533,15 +2536,7 @@ static ZEND_COLD void ZEND_FASTCALL _efree_custom(void *ptr ZEND_FILE_LINE_DC ZE zend_mm_heap *heap = AG(mm_heap); int use_custom_heap = heap->use_custom_heap; - if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { - zend_mm_observer *current = heap->observers; - while (current != NULL) { - if (current->free != NULL) { - current->free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - } - current = current->next; - } - } + HANDLE_OBSERVERS(free, ptr) if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { heap->custom_heap.debug._free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); @@ -2566,15 +2561,7 @@ static ZEND_COLD void* ZEND_FASTCALL _realloc_custom(void *ptr, size_t size ZEND // no custom memory manager, only observer present new_ptr = zend_mm_realloc_heap(AG(mm_heap), ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { - zend_mm_observer *current = heap->observers; - while (current != NULL) { - if (current->realloc != NULL) { - current->realloc(ptr, size, new_ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - } - current = current->next; - } - } + HANDLE_OBSERVERS(realloc, ptr, size, new_ptr) return new_ptr; } #endif From d96128265e4cd6ffed9f6e68a36ee2246d650854 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 31 Jul 2023 12:55:48 +0200 Subject: [PATCH 19/26] Add parameter names --- Zend/zend_alloc.c | 12 ++++++------ Zend/zend_alloc.h | 7 ++++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 3bbb44083d378..f33d7ba448182 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -203,9 +203,9 @@ typedef struct _zend_mm_huge_list zend_mm_huge_list; static bool zend_mm_use_huge_pages = false; struct _zend_mm_observer { - void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); zend_mm_observer *next; }; @@ -3098,9 +3098,9 @@ ZEND_API void zend_mm_get_custom_handlers(zend_mm_heap *heap, */ ZEND_API zend_mm_observer* zend_mm_observer_register( zend_mm_heap* heap, - void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) + void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ) { #if ZEND_MM_CUSTOM if (!heap) { diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index e61f498ad2c9c..b300ccce65288 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -287,9 +287,10 @@ typedef struct _zend_mm_observer zend_mm_observer; ZEND_API bool zend_mm_is_observed(zend_mm_heap *heap); ZEND_API zend_mm_observer* zend_mm_observer_register( zend_mm_heap *heap, - void (*malloc)(size_t, void * 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, void * ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) + void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) + ); ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer *observer); void zend_mm_observers_shutdown(zend_mm_heap *heap); From a3c4a1560c26c2e5ef7e67c285eeb13eded0e808 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 31 Jul 2023 15:20:06 +0200 Subject: [PATCH 20/26] fix test --- .../tests/zend_mm_observer_free_01.phpt | 16 +++++++++++++ .../tests/zend_mm_observer_malloc_01.phpt | 15 ++++++++++++ .../tests/zend_mm_observer_part_01.phpt | 24 ------------------- .../tests/zend_mm_observer_realloc_01.phpt | 24 +++++++++++++++++++ ext/zend_test/zend_mm_observer.c | 3 +++ 5 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 ext/zend_test/tests/zend_mm_observer_free_01.phpt create mode 100644 ext/zend_test/tests/zend_mm_observer_malloc_01.phpt delete mode 100644 ext/zend_test/tests/zend_mm_observer_part_01.phpt create mode 100644 ext/zend_test/tests/zend_mm_observer_realloc_01.phpt diff --git a/ext/zend_test/tests/zend_mm_observer_free_01.phpt b/ext/zend_test/tests/zend_mm_observer_free_01.phpt new file mode 100644 index 0000000000000..44747b9694a68 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_observer_free_01.phpt @@ -0,0 +1,16 @@ +--TEST-- +ZendMM Observer: Observe free +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zend_mm_observer.enabled=0 +opcache.enable=0 +--FILE-- + +--EXPECTREGEX-- +.*freed 0x\S+ of size \d+.* diff --git a/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt b/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt new file mode 100644 index 0000000000000..ccaeb690fd2ea --- /dev/null +++ b/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt @@ -0,0 +1,15 @@ +--TEST-- +ZendMM Observer: Observe malloc +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zend_mm_observer.enabled=0 +opcache.enable=0 +--FILE-- + +--EXPECTREGEX-- +.*malloc 0x\S+ of size \d+ \(block: \d+\).* diff --git a/ext/zend_test/tests/zend_mm_observer_part_01.phpt b/ext/zend_test/tests/zend_mm_observer_part_01.phpt deleted file mode 100644 index 31fbb4fdfcb4f..0000000000000 --- a/ext/zend_test/tests/zend_mm_observer_part_01.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -ZendMM Observer: Observe all ---EXTENSIONS-- -zend_test ---INI-- -zend_test.zend_mm_observer.enabled=0 -opcache.enable=0 ---FILE-- - ---EXPECTF-- -malloc 0x%s of size %d (block: %d) -malloc 0x%s of size %d (block: %d) -freed 0x%s of size %d diff --git a/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt b/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt new file mode 100644 index 0000000000000..f3ad440bc9824 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt @@ -0,0 +1,24 @@ +--TEST-- +ZendMM Observer: Observe realloc +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zend_mm_observer.enabled=0 +opcache.enable=0 +--FILE-- + +--EXPECTREGEX-- +.*realloc 0x\S+ of size \d+ \(block: \d+, former 0x\S+\) diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 15c79f9ac9f89..242029c4b4c47 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -28,6 +28,7 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("malloc %p of size %zu (block: %zu)\n", ptr, len, block_len); + fflush(stdout); } static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) @@ -37,6 +38,7 @@ static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LIN block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("freed %p of size %zu\n", ptr, block_len); + fflush(stdout); } static void zend_mm_test_observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) @@ -46,6 +48,7 @@ static void zend_mm_test_observer_realloc(void *ptr, size_t len, void *newptr ZE block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); + fflush(stdout); } static PHP_INI_MH(OnUpdateZendTestMMObserverEnabled) From b3d5b5ae2e00061d95b176726d89daca07b4a46d Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 1 Aug 2023 10:22:52 +0200 Subject: [PATCH 21/26] fix tests on windows --- ext/zend_test/tests/zend_mm_observer_free_01.phpt | 2 +- ext/zend_test/tests/zend_mm_observer_malloc_01.phpt | 2 +- ext/zend_test/tests/zend_mm_observer_realloc_01.phpt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/zend_test/tests/zend_mm_observer_free_01.phpt b/ext/zend_test/tests/zend_mm_observer_free_01.phpt index 44747b9694a68..1d0a9ec39c491 100644 --- a/ext/zend_test/tests/zend_mm_observer_free_01.phpt +++ b/ext/zend_test/tests/zend_mm_observer_free_01.phpt @@ -13,4 +13,4 @@ unset($string); ini_set('zend_test.zend_mm_observer.enabled', 'false'); ?> --EXPECTREGEX-- -.*freed 0x\S+ of size \d+.* +.*freed \S+ of size \d+.* diff --git a/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt b/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt index ccaeb690fd2ea..1ae00b3ab94d5 100644 --- a/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt +++ b/ext/zend_test/tests/zend_mm_observer_malloc_01.phpt @@ -12,4 +12,4 @@ $string = str_repeat("ZendMM Observer", 100); ini_set('zend_test.zend_mm_observer.enabled', 'false'); ?> --EXPECTREGEX-- -.*malloc 0x\S+ of size \d+ \(block: \d+\).* +.*malloc \S+ of size \d+ \(block: \d+\).* diff --git a/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt b/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt index f3ad440bc9824..a1f7e002812b5 100644 --- a/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt +++ b/ext/zend_test/tests/zend_mm_observer_realloc_01.phpt @@ -21,4 +21,4 @@ $a[] = 'ZendMM Observer'; ini_set('zend_test.zend_mm_observer.enabled', 'false'); ?> --EXPECTREGEX-- -.*realloc 0x\S+ of size \d+ \(block: \d+, former 0x\S+\) +.*realloc \S+ of size \d+ \(block: \d+, former \S+\) From 1a3a797211bb4c00bbf154b7ca0b1a9e09b508d9 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 2 Aug 2023 12:04:24 +0200 Subject: [PATCH 22/26] fix asan tests --- ext/zend_test/tests/zend_mm_observer_all_01.phpt | 9 ++++----- ext/zend_test/zend_mm_observer.c | 12 +++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ext/zend_test/tests/zend_mm_observer_all_01.phpt b/ext/zend_test/tests/zend_mm_observer_all_01.phpt index b26eb3d13ca6f..07f2619d06e00 100644 --- a/ext/zend_test/tests/zend_mm_observer_all_01.phpt +++ b/ext/zend_test/tests/zend_mm_observer_all_01.phpt @@ -4,12 +4,11 @@ ZendMM Observer: Observe all zend_test --INI-- zend_test.zend_mm_observer.enabled=1 -opcache.enable=0 --FILE-- --EXPECTREGEX-- -.* -ZendMM Observer enabled -.* -ZendMM Observer disabled +.*ZendMM Observer enabled.* +.*done\..* +.*ZendMM Observer disabled.* diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 242029c4b4c47..2178303629081 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -24,7 +24,7 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; - if (is_zend_ptr(ptr)) { + if (is_zend_mm() && is_zend_ptr(ptr)) { block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("malloc %p of size %zu (block: %zu)\n", ptr, len, block_len); @@ -34,7 +34,7 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; - if (is_zend_ptr(ptr)) { + if (is_zend_mm() && is_zend_ptr(ptr)) { block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("freed %p of size %zu\n", ptr, block_len); @@ -44,7 +44,7 @@ static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LIN static void zend_mm_test_observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; - if (is_zend_ptr(ptr)) { + if (is_zend_mm() && is_zend_ptr(ptr)) { block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); @@ -88,16 +88,18 @@ void zend_test_mm_observer_minit(INIT_FUNC_ARGS) void zend_test_mm_observer_rinit(void) { if (ZT_G(zend_mm_observer_enabled)) { - printf("ZendMM Observer enabled\n"); ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc); + printf("ZendMM Observer enabled\n"); + fflush(stdout); } } void zend_test_mm_observer_rshutdown(void) { if (ZT_G(observer)) { - printf("ZendMM Observer disabled\n"); zend_mm_observer_unregister(zend_mm_get_heap(), ZT_G(observer)); + printf("ZendMM Observer disabled\n"); + fflush(stdout); } ZT_G(observer) = NULL; } From b5729dab662da7448086698776c5d1300bb3ce56 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Thu, 3 Aug 2023 11:40:24 +0200 Subject: [PATCH 23/26] remove `is_zend_mm()` check --- Zend/zend_alloc.c | 2 +- ext/zend_test/zend_mm_observer.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index f33d7ba448182..76b6c48e30e77 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2420,7 +2420,7 @@ void* ZEND_FASTCALL _zend_mm_realloc2(zend_mm_heap *heap, void *ptr, size_t size ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { #if ZEND_MM_CUSTOM - if (UNEXPECTED(heap->use_custom_heap)) { + if (UNEXPECTED(heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED)) { if (heap->custom_heap.std._malloc == tracked_malloc) { zend_ulong h = ((uintptr_t) ptr) >> ZEND_MM_ALIGNMENT_LOG2; zval *size_zv = zend_hash_index_find(heap->tracked_allocs, h); diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 2178303629081..e7f7b7331743a 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -24,7 +24,7 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; - if (is_zend_mm() && is_zend_ptr(ptr)) { + if (is_zend_ptr(ptr)) { block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("malloc %p of size %zu (block: %zu)\n", ptr, len, block_len); @@ -34,7 +34,7 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; - if (is_zend_mm() && is_zend_ptr(ptr)) { + if (is_zend_ptr(ptr)) { block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("freed %p of size %zu\n", ptr, block_len); @@ -44,7 +44,7 @@ static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LIN static void zend_mm_test_observer_realloc(void *ptr, size_t len, void *newptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; - if (is_zend_mm() && is_zend_ptr(ptr)) { + if (is_zend_ptr(ptr)) { block_len = zend_mm_block_size(zend_mm_get_heap(), ptr); } printf("realloc %p of size %zu (block: %zu, former %p)\n", newptr, len, block_len, ptr); From 10cba17ddb12ab67cc07a6b96786d31b216be0f4 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Thu, 3 Aug 2023 13:39:43 +0200 Subject: [PATCH 24/26] Add missing author --- ext/zend_test/zend_mm_observer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index e7f7b7331743a..68b43dd94474e 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -10,7 +10,7 @@ | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: | + | Author: Florian Engelhardt | +----------------------------------------------------------------------+ */ From 70bdf69f1b2b11a4fafdf59ee71f86f705fd59fb Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 27 Oct 2023 11:20:43 +0200 Subject: [PATCH 25/26] observe ZendMM garbage collection --- Zend/zend_alloc.c | 41 +++++++++++-------- Zend/zend_alloc.h | 4 +- .../tests/zend_mm_observer_gc_01.phpt | 15 +++++++ ext/zend_test/zend_mm_observer.c | 22 +++++++++- 4 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 ext/zend_test/tests/zend_mm_observer_gc_01.phpt diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 76b6c48e30e77..5b072e9c7eac2 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -206,9 +206,21 @@ struct _zend_mm_observer { void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*gc)(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); zend_mm_observer *next; }; +#define HANDLE_OBSERVERS(heap, use_custom_heap, observer_function, ...) \ + if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { \ + zend_mm_observer *current = heap->observers; \ + while (current != NULL) { \ + if (current->observer_function != NULL) { \ + current->observer_function(__VA_ARGS__ ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); \ + } \ + current = current->next; \ + } \ + } + /* * Memory is retrieved from OS by chunks of fixed size 2MB. * Inside chunk it's managed by pages of fixed size 4096B. @@ -1975,7 +1987,9 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) size_t collected = 0; #if ZEND_MM_CUSTOM - if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { + int use_custom_heap = heap->use_custom_heap; + if (use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { + HANDLE_OBSERVERS(heap, use_custom_heap, gc ,0); return 0; } #endif @@ -2074,6 +2088,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) } } while (chunk != heap->main_chunk); +#if ZEND_MM_CUSTOM + HANDLE_OBSERVERS(heap, use_custom_heap, gc, collected * ZEND_MM_PAGE_SIZE); +#endif + return collected * ZEND_MM_PAGE_SIZE; } @@ -2503,17 +2521,6 @@ ZEND_API bool is_zend_ptr(const void *ptr) #if ZEND_MM_CUSTOM -#define HANDLE_OBSERVERS(observer_function, ...) \ - if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { \ - zend_mm_observer *current = heap->observers; \ - while (current != NULL) { \ - if (current->observer_function != NULL) { \ - current->observer_function(__VA_ARGS__ ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); \ - } \ - current = current->next; \ - } \ - } - static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { void *ptr; @@ -2527,7 +2534,7 @@ static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_D // no custom memory manager, only observer present ptr = zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - HANDLE_OBSERVERS(malloc, size, ptr) + HANDLE_OBSERVERS(heap, use_custom_heap, malloc, size, ptr) return ptr; } @@ -2536,7 +2543,7 @@ static ZEND_COLD void ZEND_FASTCALL _efree_custom(void *ptr ZEND_FILE_LINE_DC ZE zend_mm_heap *heap = AG(mm_heap); int use_custom_heap = heap->use_custom_heap; - HANDLE_OBSERVERS(free, ptr) + HANDLE_OBSERVERS(heap, use_custom_heap, free, ptr) if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { heap->custom_heap.debug._free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); @@ -2561,7 +2568,7 @@ static ZEND_COLD void* ZEND_FASTCALL _realloc_custom(void *ptr, size_t size ZEND // no custom memory manager, only observer present new_ptr = zend_mm_realloc_heap(AG(mm_heap), ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - HANDLE_OBSERVERS(realloc, ptr, size, new_ptr) + HANDLE_OBSERVERS(heap, use_custom_heap, realloc, ptr, size, new_ptr) return new_ptr; } #endif @@ -3100,7 +3107,8 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( zend_mm_heap* heap, void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) + void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*gc)(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ) { #if ZEND_MM_CUSTOM if (!heap) { @@ -3112,6 +3120,7 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( node->malloc = malloc; node->free = free; node->realloc = realloc; + node->gc = gc; node->next = NULL; // set bitflag for observers being around diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index b300ccce65288..9af39f9433e10 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -289,8 +289,8 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( zend_mm_heap *heap, void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) - + void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), + void (*gc)(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ); ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer *observer); void zend_mm_observers_shutdown(zend_mm_heap *heap); diff --git a/ext/zend_test/tests/zend_mm_observer_gc_01.phpt b/ext/zend_test/tests/zend_mm_observer_gc_01.phpt new file mode 100644 index 0000000000000..7d828984afcd8 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_observer_gc_01.phpt @@ -0,0 +1,15 @@ +--TEST-- +ZendMM Observer: Observe garbage collection +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zend_mm_observer.enabled=0 +opcache.enable=0 +--FILE-- + +--EXPECTREGEX-- +.*garbage collection ended with \d+ bytes collected.* diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 68b43dd94474e..14f20e547d1ce 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -31,6 +31,12 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC fflush(stdout); } +static void zend_mm_test_observer_gc(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + printf("garbage collection ended with %zu bytes collected\n", len); + fflush(stdout); +} + static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; @@ -61,7 +67,13 @@ static PHP_INI_MH(OnUpdateZendTestMMObserverEnabled) if (int_value == 1) { if (ZT_G(observer) == NULL) { - ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc); + ZT_G(observer) = zend_mm_observer_register( + zend_mm_get_heap(), + zend_mm_test_observer_malloc, + zend_mm_test_observer_free, + zend_mm_test_observer_realloc, + zend_mm_test_observer_gc + ); } } else { if (ZT_G(observer) != NULL) { @@ -88,7 +100,13 @@ void zend_test_mm_observer_minit(INIT_FUNC_ARGS) void zend_test_mm_observer_rinit(void) { if (ZT_G(zend_mm_observer_enabled)) { - ZT_G(observer) = zend_mm_observer_register(zend_mm_get_heap(), zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc); + ZT_G(observer) = zend_mm_observer_register( + zend_mm_get_heap(), + zend_mm_test_observer_malloc, + zend_mm_test_observer_free, + zend_mm_test_observer_realloc, + zend_mm_test_observer_gc + ); printf("ZendMM Observer enabled\n"); fflush(stdout); } From 8fa36be1f0abed033142de503cb7e1d8efe405de Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Fri, 27 Oct 2023 13:08:41 +0200 Subject: [PATCH 26/26] Add shutdown observer and fix debug builds --- Zend/zend_alloc.c | 38 ++++++++++++++----- Zend/zend_alloc.h | 3 +- .../tests/zend_mm_observer_shutdown_01.phpt | 12 ++++++ ext/zend_test/zend_mm_observer.c | 25 +++++++----- 4 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 ext/zend_test/tests/zend_mm_observer_shutdown_01.phpt diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 5b072e9c7eac2..f16bf1edfb979 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -206,11 +206,23 @@ struct _zend_mm_observer { void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); - void (*gc)(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); + void (*gc)(size_t len); + void (*shutdown)(bool full, bool silent); zend_mm_observer *next; }; -#define HANDLE_OBSERVERS(heap, use_custom_heap, observer_function, ...) \ +#define HANDLE_NO_DEBUG_OBSERVERS(observer_function, ...) \ + if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { \ + zend_mm_observer *current = heap->observers; \ + while (current != NULL) { \ + if (current->observer_function != NULL) { \ + current->observer_function(__VA_ARGS__); \ + } \ + current = current->next; \ + } \ + } + +#define HANDLE_OBSERVERS(observer_function, ...) \ if (use_custom_heap & ZEND_MM_CUSTOM_HEAP_OBSERVED) { \ zend_mm_observer *current = heap->observers; \ while (current != NULL) { \ @@ -1989,7 +2001,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) #if ZEND_MM_CUSTOM int use_custom_heap = heap->use_custom_heap; if (use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { - HANDLE_OBSERVERS(heap, use_custom_heap, gc ,0); + HANDLE_NO_DEBUG_OBSERVERS(gc ,0); return 0; } #endif @@ -2089,7 +2101,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) } while (chunk != heap->main_chunk); #if ZEND_MM_CUSTOM - HANDLE_OBSERVERS(heap, use_custom_heap, gc, collected * ZEND_MM_PAGE_SIZE); + HANDLE_NO_DEBUG_OBSERVERS(gc, collected * ZEND_MM_PAGE_SIZE); #endif return collected * ZEND_MM_PAGE_SIZE; @@ -2297,8 +2309,12 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) zend_mm_huge_list *list; #if ZEND_MM_CUSTOM + int use_custom_heap = heap->use_custom_heap; + + HANDLE_NO_DEBUG_OBSERVERS(shutdown, full, silent) zend_mm_observers_shutdown(heap); - if (heap->use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { + + if (use_custom_heap & ~ZEND_MM_CUSTOM_HEAP_OBSERVED) { if (heap->custom_heap.std._malloc == tracked_malloc) { if (silent) { tracked_free_all(); @@ -2314,7 +2330,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) } if (full) { - if (ZEND_DEBUG && heap->use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { + if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { heap->custom_heap.debug._free(heap ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); } else { heap->custom_heap.std._free(heap); @@ -2534,7 +2550,7 @@ static ZEND_COLD void* ZEND_FASTCALL _malloc_custom(size_t size ZEND_FILE_LINE_D // no custom memory manager, only observer present ptr = zend_mm_alloc_heap(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - HANDLE_OBSERVERS(heap, use_custom_heap, malloc, size, ptr) + HANDLE_OBSERVERS(malloc, size, ptr) return ptr; } @@ -2543,7 +2559,7 @@ static ZEND_COLD void ZEND_FASTCALL _efree_custom(void *ptr ZEND_FILE_LINE_DC ZE zend_mm_heap *heap = AG(mm_heap); int use_custom_heap = heap->use_custom_heap; - HANDLE_OBSERVERS(heap, use_custom_heap, free, ptr) + HANDLE_OBSERVERS(free, ptr) if (ZEND_DEBUG && use_custom_heap & ZEND_MM_CUSTOM_HEAP_DEBUG) { heap->custom_heap.debug._free(ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); @@ -2568,7 +2584,7 @@ static ZEND_COLD void* ZEND_FASTCALL _realloc_custom(void *ptr, size_t size ZEND // no custom memory manager, only observer present new_ptr = zend_mm_realloc_heap(AG(mm_heap), ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } - HANDLE_OBSERVERS(heap, use_custom_heap, realloc, ptr, size, new_ptr) + HANDLE_OBSERVERS(realloc, ptr, size, new_ptr) return new_ptr; } #endif @@ -3108,7 +3124,8 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (*gc)(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) + void (*gc)(size_t len), + void (*shutdown)(bool full, bool silent) ) { #if ZEND_MM_CUSTOM if (!heap) { @@ -3121,6 +3138,7 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( node->free = free; node->realloc = realloc; node->gc = gc; + node->shutdown = shutdown; node->next = NULL; // set bitflag for observers being around diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index 9af39f9433e10..759a80b29973a 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -290,7 +290,8 @@ ZEND_API zend_mm_observer* zend_mm_observer_register( void (*malloc)(size_t len, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*free)(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), void (*realloc)(void *old_ptr, size_t len, void *new_ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC), - void (*gc)(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) + void (*gc)(size_t len), + void (*shutdown)(bool full, bool silent) ); ZEND_API bool zend_mm_observer_unregister(zend_mm_heap *heap, zend_mm_observer *observer); void zend_mm_observers_shutdown(zend_mm_heap *heap); diff --git a/ext/zend_test/tests/zend_mm_observer_shutdown_01.phpt b/ext/zend_test/tests/zend_mm_observer_shutdown_01.phpt new file mode 100644 index 0000000000000..664a52af68849 --- /dev/null +++ b/ext/zend_test/tests/zend_mm_observer_shutdown_01.phpt @@ -0,0 +1,12 @@ +--TEST-- +ZendMM Observer: Observe shutdown +--EXTENSIONS-- +zend_test +--INI-- +zend_test.zend_mm_observer.enabled=1 +--FILE-- + +--EXPECTREGEX-- +.*shutdown in progress: full\(0\), silent\(1\).* diff --git a/ext/zend_test/zend_mm_observer.c b/ext/zend_test/zend_mm_observer.c index 14f20e547d1ce..7ea7ef7ce2b24 100644 --- a/ext/zend_test/zend_mm_observer.c +++ b/ext/zend_test/zend_mm_observer.c @@ -31,12 +31,6 @@ static void zend_mm_test_observer_malloc(size_t len, void *ptr ZEND_FILE_LINE_DC fflush(stdout); } -static void zend_mm_test_observer_gc(size_t len ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) -{ - printf("garbage collection ended with %zu bytes collected\n", len); - fflush(stdout); -} - static void zend_mm_test_observer_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t block_len = 0; @@ -57,6 +51,18 @@ static void zend_mm_test_observer_realloc(void *ptr, size_t len, void *newptr ZE fflush(stdout); } +static void zend_mm_test_observer_gc(size_t len) +{ + printf("garbage collection ended with %zu bytes collected\n", len); + fflush(stdout); +} + +static void zend_mm_test_observer_shutdown(bool full, bool silent) +{ + printf("shutdown in progress: full(%d), silent(%d)\n", full, silent); + fflush(stdout); +} + static PHP_INI_MH(OnUpdateZendTestMMObserverEnabled) { if (new_value == NULL) { @@ -72,7 +78,8 @@ static PHP_INI_MH(OnUpdateZendTestMMObserverEnabled) zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc, - zend_mm_test_observer_gc + zend_mm_test_observer_gc, + zend_mm_test_observer_shutdown ); } } else { @@ -105,7 +112,8 @@ void zend_test_mm_observer_rinit(void) zend_mm_test_observer_malloc, zend_mm_test_observer_free, zend_mm_test_observer_realloc, - zend_mm_test_observer_gc + zend_mm_test_observer_gc, + zend_mm_test_observer_shutdown ); printf("ZendMM Observer enabled\n"); fflush(stdout); @@ -121,4 +129,3 @@ void zend_test_mm_observer_rshutdown(void) } ZT_G(observer) = NULL; } -