Memory leaks using MagickWand C API

The MagickWand interface is a new high-level C API interface to ImageMagick core methods. We discourage the use of the core methods and encourage the use of this API instead. Post MagickWand questions, bug reports, and suggestions to this forum.
Post Reply
mikko

Memory leaks using MagickWand C API

Post by mikko »

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:

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
Post Reply