Writing PHP Extensions1. Setting up Your PHP Build Environment on Linux2. Generating a PHP Extension Skeleton3. Building and Installing a PHP Extension4. Rebuilding Extensions for Production5. Extension Skeleton File Content6. Running PHP Extension Tests7. Adding New Functionality8. Basic PHP Structures9. PHP Arrays10. Catching Memory Leaks11. PHP Memory Management12. PHP References13. Copy on Write14. PHP Classes and Objects15. Using OOP in our Example Extension16. Embedding C Data into PHP Objects17. Overriding Object Handlers18. Answers to Common Extension Questions10. Catching Memory LeaksLet’s try to pass array with a value of some unexpected type: $ php -r ‘var_dump(test_scale([null]));’ Warning: test_scale(): unexpected argument type in Command line code on line 1 NULL [Wed Jan 22 13:56:11 2020] Script: ‘Standard input code’ /home/dmitry/tmp/php-src/Zend/zend_hash.c(256) : Freeing 0x00007f8189c57840 (56 bytes), script=Standard input code === Total 1 memory leaks detected ===We see our expected warning and NULL result, but then we see some debug info about leaked memory from internal PHP memory debugger. Note, that this information is only available in DEBUG PHP build, and this is one of the reasons, I recommend, to use DEBUG build during development. The information above says that 56-bytes of memory allocated on line 256 of Zend/zend_hash.c was leaked. This is the body of _zend_new_array() and we may already guess where it’s called from, because we call it just once. However, in real-life we can’t be sure about the call site, and it would be great to get a back-trace of the leaked allocation. On Linux we may use valgrind. It’s a great tool, that can catch memory-leaks and other incorrect memory access problems (e.g. use-after-free and out-of-boundary). Valgrind emulates the program with an overridden system memory manager (malloc, free and related functions) and catches inconsistencies. Looking ahead, PHP uses its own memory manager and we should switch to system one, using USE_ZEND_ALLOC environment variable. It also makes sense to disable extension unloading.$ USE_ZEND_ALLOC=0 ZEND_DONT_UNLOAD_MODULES=1 valgrind --leak-check=full \ php -r ‘var_dump(test_scale([null]));’ ... ==19882== 56 bytes in 1 blocks are definitely lost in loss record 19 of 27 ==19882== at 0x483880B: malloc (vg_replace_malloc.c:309) ==19882== by 0x997CC5: __zend_malloc (zend_alloc.c:2975) ==19882== by 0x996C30: _malloc_custom (zend_alloc.c:2416) ==19882== by 0x996D6E: _emalloc (zend_alloc.c:2535) ==19882== by 0x9E13BE: _zend_new_array (zend_hash.c:256) ==19882== by 0x4849AE0: do_scale (test.c:66) ==19882== by 0x4849F69: zif_test_scale (test.c:100) ==19882== by 0xA3CE1B: ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER (zend_vm_execute.h:1313) ==19882== by 0xA9D0E8: execute_ex (zend_vm_execute.h:53564) ==19882== by 0xAA11A0: zend_execute (zend_vm_execute.h:57664) ==19882== by 0x9B7D0B: zend_eval_stringl (zend_execute_API.c:1082) ==19882== by 0x9B7EBF: zend_eval_stringl_ex (zend_execute_API.c:1123) ...Now we can be sure: the source of our memory-leak is a call of zend_new_array() function from our do_scale(). To fix it, we should destroy the array in case of FAILURE. } else if (Z_TYPE_P(x) == IS_ARRAY) { zend_array *ret = zend_new_ array(zend_array_count(Z_ARR_P(x))); zend_ulong idx; zend_string *key; zval *val, tmp; ZEND_HASH_FOREACH_KEY_VAL(Z_ARR_P(x), idx, key, val) { if (do_scale(&tmp, val, factor) != SUCCESS) { zend_array_destroy(ret); return FAILURE; } if (key) { zend_hash_add(ret, key, &tmp); } else { zend_hash_index_add(ret, idx, &tmp); } } ZEND_HASH_FOREACH_END(); RETVAL_ARR(ret); } else {Don’t forget to test this. Valgrind is much smarter then the internal PHP memory debugger and in case you cover your extension with *.phpt regression tests, you may run all of them under valgrind. $ make test TESTS=”-m”Request PDF VersionBook traversal links for 10. Catching Memory Leaks‹ 9. PHP ArraysWriting PHP Extensions11. PHP Memory Management ›