adtools/clib2

View on GitHub
library/stdlib_free.c

Summary

Maintainability
Test Coverage
/*
 * $Id: stdlib_free.c,v 1.13 2006-01-08 12:04:25 obarthel Exp $
 *
 * :ts=4
 *
 * Portable ISO 'C' (1994) runtime library for the Amiga computer
 * Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Neither the name of Olaf Barthel nor the names of contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*#define DEBUG*/

#ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */

/****************************************************************************/

#ifndef _STDLIB_MEMORY_H
#include "stdlib_memory.h"
#endif /* _STDLIB_MEMORY_H */

/****************************************************************************/

#undef free
#undef __free

/****************************************************************************/

#ifdef __MEM_DEBUG

/****************************************************************************/

STATIC char
get_hex_char(int n)
{
    char result;

    if(0 <= n && n <= 9)
        result = n + '0';
    else
        result = n + 'A' - 10;

    return(result);
}

STATIC VOID
int_to_hex(unsigned long v,char * buffer,int min_digits)
{
    int i,j,c;

    i = 0;

    do
    {
        c = (v % 16);
        v = v / 16;

        buffer[i++] = get_hex_char(c);
    }
    while(v > 0);

    while(i < min_digits)
        buffer[i++] = '0';

    for(j = 0 ; j < i / 2 ; j++)
    {
        c                    = buffer[i - 1 - j];
        buffer[i - 1 - j]    = buffer[j];
        buffer[j]            = c;
    }

    buffer[i] = '\0';
}

STATIC VOID
dump_memory(unsigned char * m,int size,int ignore)
{
    const int mod = 20;
    int position,i,c;
    char buffer[120];
    char hex[10];

    buffer[0] = '\0';

    for(i = 0 ; i < size ; i++)
    {
        position = i % mod;
        if(position == 0)
        {
            if(buffer[0] != '\0')
            {
                int len = sizeof(buffer)-1;

                while(len > 0 && buffer[len-1] == ' ')
                    len--;

                buffer[len] = '\0';

                kprintf("[%s] %s\n",__program_name,buffer);
            }

            memset(buffer,' ',sizeof(buffer)-1);

            int_to_hex((unsigned long)&m[i],hex,8);

            memmove(buffer,hex,8);
            hex[9] = ':';
        }

        if(m[i] != ignore)
        {
            buffer[10 + 2 * position + 0] = get_hex_char(m[i] >> 4);
            buffer[10 + 2 * position + 1] = get_hex_char(m[i] & 15);

            c = m[i];
            if(c < ' ' || (c >= 127 && c <= 160))
                c = '.';

            buffer[10 + 2 * mod + 1 + position] = c;
        }
        else
        {
            buffer[10 + 2 * position + 0] = '_';
            buffer[10 + 2 * position + 1] = '_';
        }
    }

    if(buffer[0] != '\0')
    {
        int len = sizeof(buffer)-1;

        while(len > 0 && buffer[len-1] == ' ')
            len--;

        buffer[len] = '\0';

        kprintf("[%s] %s\n",__program_name,buffer);
    }
}

/****************************************************************************/

STATIC VOID
check_memory_node(struct MemoryNode * mn,const char * file,int line)
{
    ULONG size = GET_MN_SIZE(mn);
    unsigned char * head = (unsigned char *)(mn + 1);
    unsigned char * body = head + MALLOC_HEAD_SIZE;
    unsigned char * tail = body + size;
    int max_head_damage = 0;
    int max_tail_damage = 0;
    int max_body_damage = 0;
    int i;

    for(i = 1 ; i <= MALLOC_HEAD_SIZE ; i++)
    {
        if(head[MALLOC_HEAD_SIZE - i] != MALLOC_HEAD_FILL)
            max_head_damage = i;
    }

    if(max_head_damage > 0)
    {
        kprintf("[%s] ",__program_name);

        if(file != NULL)
            kprintf("%s:%ld:",file,line);

        kprintf("At least %ld bytes were damaged in front of allocation 0x%08lx..0x%08lx, size = %ld",
            max_head_damage,
            body,body + size - 1,size);

        if(mn->mn_File != NULL)
            kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);

        kprintf("\n");

        dump_memory(head,MALLOC_HEAD_SIZE,MALLOC_HEAD_FILL);
    }

    for(i = 0 ; i < MALLOC_TAIL_SIZE ; i++)
    {
        if(tail[i] != MALLOC_TAIL_FILL)
            max_tail_damage = i+1;
    }

    if(max_tail_damage > 0)
    {
        kprintf("[%s] ",__program_name);

        if(file != NULL)
            kprintf("%s:%ld:",file,line);

        kprintf("At least %ld bytes were damaged behind allocation 0x%08lx..0x%08lx, size = %ld (with damage = %ld)",
            max_tail_damage,
            body,body + size - 1,
            size,size+max_tail_damage);

        if(mn->mn_File != NULL)
            kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);

        kprintf("\n");

        dump_memory(tail,MALLOC_TAIL_SIZE,MALLOC_TAIL_FILL);
    }

    if(mn->mn_AlreadyFree)
    {
        ULONG j;

        for(j = 0 ; j < size ; j++)
        {
            if(body[j] != MALLOC_FREE_FILL)
                max_body_damage = j+1;
        }

        if(max_body_damage > 0)
        {
            kprintf("[%s] ",__program_name);

            if(file != NULL)
                kprintf("%s:%ld:",file,line);

            kprintf("At least %ld bytes were damaged in freed allocation 0x%08lx..0x%08lx, size = %ld",
                max_body_damage,
                body,body + size - 1,size);

            if(mn->mn_File != NULL)
                kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);

            kprintf("\n");

            dump_memory(body,size,MALLOC_FREE_FILL);
        }
    }
}

/****************************************************************************/

void
__check_memory_allocations(const char * file,int line)
{
    struct MemoryNode * mn;

    __memory_lock();

    for(mn = (struct MemoryNode *)__memory_list.mlh_Head ;
        mn->mn_MinNode.mln_Succ != NULL ;
        mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
    {
        check_memory_node(mn,file,line);
    }

    __memory_unlock();
}

/****************************************************************************/

struct MemoryNode *
__find_memory_node(void * address)
{
    struct MemoryNode * result;

    assert( address != NULL );

    __memory_lock();

    #if defined(__USE_MEM_TREES)
    {
        result = __red_black_tree_find(&__memory_tree,address);
    }
    #else
    {
        struct MemoryNode * mn;

        result = NULL;

        for(mn = (struct MemoryNode *)__memory_list.mlh_Head ;
            mn->mn_MinNode.mln_Succ != NULL ;
            mn = (struct MemoryNode *)mn->mn_MinNode.mln_Succ)
        {
            if(address == mn->mn_Allocation)
            {
                result = mn;
                break;
            }
        }
    }
    #endif /* __USE_MEM_TREES */

    __memory_unlock();

    return(result);
}

/****************************************************************************/

#else

/****************************************************************************/

struct MemoryNode *
__find_memory_node(void * address)
{
    struct MemoryNode * result;

    assert( address != NULL );

    result = &((struct MemoryNode *)address)[-1];

    return(result);
}

/****************************************************************************/

#endif /* __MEM_DEBUG */

/****************************************************************************/

STATIC VOID
remove_and_free_memory_node(struct MemoryNode * mn)
{
    size_t allocation_size;

    assert( mn != NULL );

    __memory_lock();

    #if defined(__MEM_DEBUG)
    {
        Remove((struct Node *)mn);

        #if defined(__USE_MEM_TREES)
        {
            __red_black_tree_remove(&__memory_tree,mn);
        }
        #endif /* __USE_MEM_TREES */

        allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + GET_MN_SIZE(mn) + MALLOC_TAIL_SIZE;

        assert( allocation_size == mn->mn_AllocationSize );

        memset(mn,MALLOC_FREE_FILL,allocation_size);
    }
    #else
    {
        allocation_size = sizeof(*mn) + GET_MN_SIZE(mn);
    }
    #endif /* __MEM_DEBUG */

    #if defined(__USE_SLAB_ALLOCATOR)
    {
        /* Are we using the slab allocator? */
        if(__slab_data.sd_InUse)
        {
            __slab_free(mn,allocation_size);
        }
        else
        {
            if(__memory_pool != NULL)
            {
                PROFILE_OFF();
                FreePooled(__memory_pool,mn,allocation_size);
                PROFILE_ON();
            }
            else
            {
                #if defined(__MEM_DEBUG)
                {
                    PROFILE_OFF();
                    FreeMem(mn,allocation_size);
                    PROFILE_ON();
                }
                #else
                {
                    struct MinNode * mln = (struct MinNode *)mn;

                    mln--;

                    Remove((struct Node *)mln);

                    PROFILE_OFF();
                    FreeMem(mln,sizeof(*mln) + allocation_size);
                    PROFILE_ON();
                }
                #endif /* __MEM_DEBUG */
            }
        }
    }
    #else
    {
        if(__memory_pool != NULL)
        {
            PROFILE_OFF();
            FreePooled(__memory_pool,mn,allocation_size);
            PROFILE_ON();
        }
        else
        {
            #if defined(__MEM_DEBUG)
            {
                PROFILE_OFF();
                FreeMem(mn,allocation_size);
                PROFILE_ON();
            }
            #else
            {
                struct MinNode * mln = (struct MinNode *)mn;

                mln--;

                Remove((struct Node *)mln);

                PROFILE_OFF();
                FreeMem(mln,sizeof(*mln) + allocation_size);
                PROFILE_ON();
            }
            #endif /* __MEM_DEBUG */
        }
    }
    #endif /* __USE_SLAB_ALLOCATOR */

    __current_memory_allocated -= allocation_size;
    __current_num_memory_chunks_allocated--;

    __memory_unlock();
}

/****************************************************************************/

void
__free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED line)
{
    assert(mn != NULL);

    #ifdef __MEM_DEBUG
    {
        ULONG size = GET_MN_SIZE(mn);

        check_memory_node(mn,file,line);

        if(NOT mn->mn_AlreadyFree)
        {
            #ifdef __MEM_DEBUG_LOG
            {
                kprintf("[%s] - %10ld 0x%08lx [",__program_name,size,mn->mn_Allocation);

                if(mn->mn_File != NULL)
                    kprintf("allocated at %s:%ld, ",mn->mn_File,mn->mn_Line);

                kprintf("freed at %s:%ld]\n",file,line);
            }
            #endif /* __MEM_DEBUG_LOG */

            if(__never_free)
            {
                mn->mn_AlreadyFree = TRUE;

                mn->mn_FreeFile    = (char *)file;
                mn->mn_FreeLine = line;

                memset(mn->mn_Allocation,MALLOC_FREE_FILL,size);
            }
            else
            {
                remove_and_free_memory_node(mn);
            }
        }
        else
        {
            #ifdef __MEM_DEBUG_LOG
            {
                kprintf("[%s] - %10ld 0x%08lx [",__program_name,size,mn->mn_Allocation);

                kprintf("FAILED]\n");
            }
            #endif /* __MEM_DEBUG_LOG */

            kprintf("[%s] %s:%ld:Allocation at address 0x%08lx, size %ld",
                __program_name,file,line,mn->mn_Allocation,size);

            if(mn->mn_File != NULL)
                kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);

            kprintf(", has already been freed at %s:%ld.\n",mn->mn_FreeFile,mn->mn_FreeLine);
        }
    }
    #else
    {
        remove_and_free_memory_node(mn);
    }
    #endif /* __MEM_DEBUG */
}

/****************************************************************************/

VOID
__free_memory(void * ptr,BOOL force,const char * file,int line)
{
    struct MemoryNode * mn;

    assert(ptr != NULL);

    SHOWPOINTER(ptr);
    SHOWVALUE(force);

    #ifdef __MEM_DEBUG
    {
        /*if((rand() % 16) == 0)
            __check_memory_allocations(file,line);*/
    }
    #endif /* __MEM_DEBUG */

    mn = __find_memory_node(ptr);

    #ifdef __MEM_DEBUG
    {
        if(mn != NULL)
        {
            if(force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE))
                __free_memory_node(mn,file,line);
        }
        else
        {
            #ifdef __MEM_DEBUG_LOG
            {
                kprintf("[%s] - %10ld 0x%08lx [",__program_name,0,ptr);

                kprintf("FAILED]\n");
            }
            #endif /* __MEM_DEBUG_LOG */

            kprintf("[%s] %s:%ld:Address for free(0x%08lx) not known.\n",__program_name,file,line,ptr);

            D(("memory allocation at 0x%08lx could not be freed",ptr));
        }
    }
    #else
    {
        assert( mn != NULL );

        SHOWVALUE(mn->mn_Size);

        if(mn != NULL && (force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE)))
            __free_memory_node(mn,file,line);
    }
    #endif /* __MEM_DEBUG */
}

/****************************************************************************/

__static void
__free(void * ptr,const char * file,int line)
{
    __memory_lock();

    /* Try to get rid of now unused memory. */
    if(__alloca_cleanup != NULL)
        (*__alloca_cleanup)(file,line);

    __memory_unlock();

    if(ptr != NULL)
        __free_memory(ptr,FALSE,file,line);
}

/****************************************************************************/

void
free(void * ptr)
{
    __free(ptr,NULL,0);
}