Memory leaks using MagickWand C API
Posted: 2006-12-20T17:05:46-07:00
Im trying to learn PHP5 extension development and I chose to create a test extension using imagemagick c api. Seems like everything is not goin as it should:
==7878== still reachable: 760 bytes in 24 blocks.
From valgrind output. Looks like im not freeing some resources I should. Im not much of C coder and higher level languages have teached me to rely on automatic garbage collection. I know I'm not freeing something I should but what bothers me is this:
From what I can tell I'm losing memory by issuing MagickWandGenesis();
Heres my extension code (it's a bit of a mess because I've been banging my head against the wall for a while now)
==7878== still reachable: 760 bytes in 24 blocks.
From valgrind output. Looks like im not freeing some resources I should. Im not much of C coder and higher level languages have teached me to rely on automatic garbage collection. I know I'm not freeing something I should but what bothers me is this:
Code: Select all
dev:/home/mikko/php-5.2.0# USE_ZEND_ALLOC=0 valgrind --show-reachable=yes --leak-check=full php ../magick.php
==7878== Memcheck, a memory error detector.
==7878== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
==7878== Using LibVEX rev 1658, a library for dynamic binary translation.
==7878== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
==7878== Using valgrind-3.2.1-Debian, a dynamic binary instrumentation framework.
==7878== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
==7878== For more details, rerun with: -v
==7878==
==7878==
==7878== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 35 from 1)
==7878== malloc/free: in use at exit: 760 bytes in 24 blocks.
==7878== malloc/free: 10,182 allocs, 10,158 frees, 1,241,516 bytes allocated.
==7878== For counts of detected errors, rerun with: -v
==7878== searching for pointers to 24 not-freed blocks.
==7878== checked 510,528 bytes.
==7878==
==7878== 64 bytes in 2 blocks are still reachable in loss record 1 of 3
==7878== at 0x401C38B: malloc (vg_replace_malloc.c:149)
==7878== by 0x41EDE43: lt_emalloc (ltdl.c:1019)
==7878== by 0x41EE737: lt_dlloader_add (ltdl.c:4328)
==7878== by 0x41F009A: lt_dlinit (ltdl.c:2227)
==7878== by 0x4193E50: GetModuleInfo (module.c:920)
==7878== by 0x4192318: GetMagickInfo (magick.c:1072)
==7878== by 0x4192706: InitializeMagick (magick.c:969)
==7878== by 0x40E8F0D: AcquireWandId (wand.c:90)
==7878== by 0x40B7CD6: MagickWandGenesis (magick-wand.c:875)
==7878== by 0x80EDDF9: php_magickwand_object_new (magickwand.c:320)
==7878== by 0x825A5E9: _object_and_properties_init (zend_API.c:950)
==7878== by 0x825A6DD: _object_init_ex (zend_API.c:957)
==7878==
==7878==
==7878== 252 bytes in 7 blocks are still reachable in loss record 2 of 3
==7878== at 0x401C38B: malloc (vg_replace_malloc.c:149)
==7878== by 0x41C8BF5: AllocateSemaphoreInfo (semaphore.c:173)
==7878== by 0x41C8D58: AcquireSemaphoreInfo (semaphore.c:131)
==7878== by 0x40E8EA9: AcquireWandId (wand.c:83)
==7878== by 0x40B7CD6: MagickWandGenesis (magick-wand.c:875)
==7878== by 0x80EDDF9: php_magickwand_object_new (magickwand.c:320)
==7878== by 0x825A5E9: _object_and_properties_init (zend_API.c:950)
==7878== by 0x825A6DD: _object_init_ex (zend_API.c:957)
==7878== by 0x8270CF5: ZEND_NEW_SPEC_HANDLER (zend_vm_execute.h:406)
==7878== by 0x82714C7: execute (zend_vm_execute.h:92)
==7878== by 0x8255C39: zend_execute_scripts (zend.c:1097)
==7878== by 0x82193DF: php_execute_script (main.c:1758)
==7878==
==7878==
==7878== 444 bytes in 15 blocks are still reachable in loss record 3 of 3
==7878== at 0x401C38B: malloc (vg_replace_malloc.c:149)
==7878== by 0x4193310: AcquireMagickMemory (memory.c:321)
==7878== by 0x41CF135: NewSplayTree (splay-tree.c:830)
==7878== by 0x40E8EE5: AcquireWandId (wand.c:87)
==7878== by 0x40B7CD6: MagickWandGenesis (magick-wand.c:875)
==7878== by 0x80EDDF9: php_magickwand_object_new (magickwand.c:320)
==7878== by 0x825A5E9: _object_and_properties_init (zend_API.c:950)
==7878== by 0x825A6DD: _object_init_ex (zend_API.c:957)
==7878== by 0x8270CF5: ZEND_NEW_SPEC_HANDLER (zend_vm_execute.h:406)
==7878== by 0x82714C7: execute (zend_vm_execute.h:92)
==7878== by 0x8255C39: zend_execute_scripts (zend.c:1097)
==7878== by 0x82193DF: php_execute_script (main.c:1758)
==7878==
==7878== LEAK SUMMARY:
==7878== definitely lost: 0 bytes in 0 blocks.
==7878== possibly lost: 0 bytes in 0 blocks.
==7878== still reachable: 760 bytes in 24 blocks.
==7878== suppressed: 0 bytes in 0 blocks.
From what I can tell I'm losing memory by issuing MagickWandGenesis();
Heres my extension code (it's a bit of a mess because I've been banging my head against the wall for a while now)
Code: Select all
#include "php_magickwand.h"
#include "Zend/zend_exceptions.h"
#define PHP_MAGICKWAND_SC_NAME "MagickWand"
/* Structure for magickwand object. */
typedef struct _php_magickwand_object {
zend_object zo;
MagickWand *magick_wand;
} php_magickwand_object;
// Handlers
static zend_object_handlers magickwand_object_handlers;
// Class entry
zend_class_entry *php_magickwand_sc_entry,
*php_magickwand_exception_class_entry;
PHP_METHOD(MagickWand, __construct)
{
}
void throwMagickWandException( MagickWand *magick_wand, long code )
{
ExceptionType severity;
char *description;
description = MagickGetException( magick_wand, &severity );
zend_throw_exception( php_magickwand_exception_class_entry, description, (long)code TSRMLS_CC);
description = (char *)MagickRelinquishMemory( description );
}
PHP_METHOD(MagickWand, readimagefile)
{
char *fileName;
int fileNameLen;
zval *object;
MagickBooleanType status;
php_magickwand_object *intern;
if ( ZEND_NUM_ARGS() != 1 )
{
ZEND_WRONG_PARAM_COUNT();
}
// Parse parameters given to function
if (zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &fileName, &fileNameLen ) == FAILURE )
{
return;
}
object = getThis();
intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
status = MagickReadImage( intern->magick_wand, fileName );
// No magick is going to happen
if ( status == MagickFalse )
{
throwMagickWandException( intern->magick_wand, 1 );
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_METHOD(MagickWand, scaleimage)
{
int x, y;
php_magickwand_object *intern;
zval *object;
MagickBooleanType status;
if ( ZEND_NUM_ARGS() != 2 )
{
ZEND_WRONG_PARAM_COUNT();
}
// Parse parameters given to function
if (zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ll", &x, &y ) == FAILURE )
{
return;
}
object = getThis();
intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
status = MagickScaleImage( intern->magick_wand, x, y );
// No magick is going to happen
if ( status == MagickFalse )
{
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_METHOD(MagickWand, resetiterator)
{
php_magickwand_object *intern;
zval *object;
MagickBooleanType status;
object = getThis();
intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
status = IsMagickWand( intern->magick_wand );
// No magick is going to happen
if ( status == MagickFalse )
{
RETURN_FALSE;
}
MagickResetIterator( intern->magick_wand );
RETURN_TRUE;
}
PHP_METHOD(MagickWand, previousimage)
{
php_magickwand_object *intern;
zval *object;
MagickBooleanType status;
object = getThis();
intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
status = MagickPreviousImage(intern->magick_wand);
// No magick is going to happen
if ( status == MagickFalse )
{
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_METHOD(MagickWand, nextimage)
{
php_magickwand_object *intern;
zval *object;
MagickBooleanType status;
object = getThis();
intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
status = MagickNextImage(intern->magick_wand);
// No magick is going to happen
if ( status == MagickFalse )
{
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_METHOD(MagickWand, getimagefilename)
{
php_magickwand_object *intern;
zval *object;
MagickBooleanType status;
char *imageName;
object = getThis();
intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
imageName = (char *)MagickGetImageFilename( intern->magick_wand );
RETVAL_STRINGL( imageName, sizeof( imageName ), 1 );
return;
}
PHP_METHOD(MagickWand, setformat)
{
char *format;
int formatLen;
zval *object;
MagickBooleanType status;
if ( ZEND_NUM_ARGS() != 1 )
{
ZEND_WRONG_PARAM_COUNT();
}
// Parse parameters given to function
if (zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &format, &formatLen ) == FAILURE )
{
return;
}
object = getThis();
php_magickwand_object *intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
status = MagickSetFormat( intern->magick_wand, format );
// No magick is going to happen
if ( status == MagickFalse )
{
RETURN_FALSE;
}
RETURN_TRUE;
}
PHP_METHOD(MagickWand, output)
{
char *fileName;
int fileNameLen;
zval *object;
object = getThis();
MagickBooleanType status;
unsigned char *returnString;
size_t returnLength = 0;
php_magickwand_object *intern = (php_magickwand_object *)zend_object_store_get_object(object TSRMLS_CC);
if ( ZEND_NUM_ARGS() != 0 && ZEND_NUM_ARGS() != 1 )
{
ZEND_WRONG_PARAM_COUNT();
}
if ( ZEND_NUM_ARGS() == 1 )
{
// Parse parameters given to function
if (zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &fileName, &fileNameLen ) == FAILURE )
{
return;
}
status = MagickWriteImage( intern->magick_wand, fileName );
// No magick is going to happen
if ( status == MagickFalse )
{
php_printf( "write failed" );
RETURN_FALSE;
}
RETURN_TRUE;
}
else
{
// Assing blob to return string
returnString = (unsigned char *) MagickGetImageBlob( intern->magick_wand, &returnLength );
// Assing this to zval
RETVAL_STRINGL( returnString, returnLength, 1 );
// Free memory
returnString = (unsigned char *)MagickRelinquishMemory( returnString );
return;
}
}
static void php_magickwand_object_free_storage(void *object TSRMLS_DC)
{
php_magickwand_object *intern = (php_magickwand_object *)object;
MagickBooleanType status;
status = IsMagickWand( intern->magick_wand );
if ( status == MagickTrue )
{
intern->magick_wand = DestroyMagickWand( intern->magick_wand );
intern->magick_wand = NULL;
}
if (intern->zo.guards)
{
zend_hash_destroy( intern->zo.guards );
FREE_HASHTABLE( intern->zo.guards );
}
if (intern->zo.properties)
{
zend_hash_destroy( intern->zo.properties );
FREE_HASHTABLE( intern->zo.properties );
}
efree( object );
efree( intern );
}
static zend_object_value php_magickwand_object_new(zend_class_entry *class_type TSRMLS_DC)
{
zval *tmp;
zend_object_value retval;
// MagickWand *magick_wand;
php_magickwand_object *intern;
// Setup magickwand env
MagickWandGenesis();
// Allocate memory for it
intern = emalloc( sizeof( php_magickwand_object ) );
memset( &intern->zo, 0, sizeof( php_magickwand_object ) );
// Set the magickwand
intern->magick_wand = NewMagickWand();
// Initialize the zend object
ALLOC_HASHTABLE(intern->zo.properties);
zend_object_std_init(&intern->zo, class_type TSRMLS_CC);
zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref,(void *) &tmp, sizeof(zval *));
retval.handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) php_magickwand_object_free_storage, NULL TSRMLS_CC);
retval.handlers = (zend_object_handlers *) &magickwand_object_handlers;
return retval;
}
static function_entry php_magickwand_functions[] = {
PHP_ME(MagickWand, __construct, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MagickWand, readimagefile, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MagickWand, setformat, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MagickWand, scaleimage, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MagickWand, output, NULL, ZEND_ACC_PUBLIC)
{ NULL, NULL, NULL }
};
PHP_MINIT_FUNCTION(initializeMagickWand)
{
zend_class_entry ce;
/*
Initialize exceptions
*/
INIT_CLASS_ENTRY(ce, "MagickWandException", NULL);
php_magickwand_exception_class_entry = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC);
php_magickwand_exception_class_entry->ce_flags |= ZEND_ACC_FINAL;
/*
Initialize normal flow
*/
INIT_CLASS_ENTRY(ce, PHP_MAGICKWAND_SC_NAME, php_magickwand_functions);
ce.create_object = php_magickwand_object_new;
php_magickwand_sc_entry = zend_register_internal_class(&ce TSRMLS_CC);
/*
Allocate some memory
*/
memcpy( &magickwand_object_handlers, zend_get_std_object_handlers(), sizeof( zend_object_handlers ) );
magickwand_object_handlers.clone_obj = NULL;
return SUCCESS;
}
PHP_MINFO_FUNCTION(magickwand_phpinfo)
{
php_info_print_table_start();
php_info_print_table_row( 2, "MagickWand Test Module", "enabled" );
php_info_print_table_row( 2, "version", PHP_MAGICKWAND_EXTVER );
php_info_print_table_end();
}
PHP_MSHUTDOWN_FUNCTION(wandTerminus)
{
// Destroy the magick wand env
MagickWandTerminus();
return( SUCCESS );
}
zend_module_entry magickwand_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_MAGICKWAND_EXTNAME,
php_magickwand_functions, /* Functions */
PHP_MINIT(initializeMagickWand), /* MINIT */
PHP_MSHUTDOWN(wandTerminus), /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
PHP_MINFO(magickwand_phpinfo), /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
PHP_MAGICKWAND_EXTVER,
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_MAGICKWAND
ZEND_GET_MODULE(magickwand)
#endif