Page 1 of 1

Memory leaks using MagickWand C API

Posted: 2006-12-20T17:05:46-07:00
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