1
0
mirror of https://github.com/adtools/clib2.git synced 2025-12-08 14:59:05 +00:00

10 Commits

Author SHA1 Message Date
5617c0eacf Slab allocator update
Unused slabs which get recycled are no longer reinitialized from scratch if their chunk size matches what the allocator needed. If the chunk size matches, the list of available chunks is left unchanged, and just the various counters are reset.

Added __get_slab_stats() function.

Added support for global __slab_purge_threshold tuning variable.

Added a short test program for the slab allocator.

The malloc-test program was linked against the wrong object file in GNUmakefile.68k. Fixed.
2016-11-27 15:53:40 +01:00
ac710b333e Accidentally omitted from version 1.211 2016-11-24 09:45:35 +01:00
d2acae7cd7 Slab allocator update
Added more consistency checking to the slab allocator, which is built if DEBUG is defined in "stdlib_slab.c".

Memory allocations are no longer guaranteed to be aligned to 64 bit word boundaries. In fact, this has not even worked reliably in the past 10 years.

Memory allocation request sizes are now rounded to multiples of 32 bit words (the size of an address pointer) instead to the size of a 64 bit word.

Reduced the memory footprint of the memory allocation management data structures by reusing the most significant bit of the memory allocation size. This allows many more allocations to fit into the 32 byte chunk slabs, but limits the maximum memory allocation size to a little less than 2 GBytes.

Added integer overflow checks to the memory management code.

Reduced the memory management overhead further. This cuts an additional 8 bytes per allocation, unless neither the slab allocator nor memory pools are available. With this reduction the slab allocator is able to use 16 byte chunks, which cover memory allocation requests of 1..8 bytes.

Fixed a bug caused by returning an allocation back to a slab which passed the wrong pointer.
2016-11-23 16:37:46 +01:00
f8cf752e6a Merge branch 'master' of https://github.com/adtools/clib2 2016-11-22 11:07:46 +01:00
0c5b88d2d3 Slab allocator changes
If the first slab in the list of slabs which share the same chunk size has no more room, it means that all other slabs following it have no room either. This speeds up the test to find a slab with free space, which can now abort and directly proceed to allocate memory for a new slab.

If an empty slab's decay count hits zero, it is moved to the front of the empty slab list to be reclaimed more quickly.

Allocations made from the slab now carry a pointer back to the slab which they are a part of. This speeds up deallocation but has the downside of making the smallest usable slab chunk size 64 bytes, which is double what used to be the minimum before.
2016-11-22 11:07:38 +01:00
1ea8953bd3 Added __get_slab_allocations() function
__get_slab_allocations() which will report information about each memory allocation made by the slab allocator which does not come from a slab.
2016-11-22 11:06:29 +01:00
ff908f8a02 Added a malloc test program 2016-11-22 10:54:58 +01:00
525e193113 added missing .codeclimate.yml and .travis.yml 2016-11-22 10:52:01 +01:00
2df2393b81 minor tweak 2016-11-22 10:50:03 +01:00
ecd40943e2 added Travis-CI and CodeClimate code check support. 2016-11-22 10:46:01 +01:00
34 changed files with 1708 additions and 275 deletions

14
.codeclimate.yml Normal file
View File

@ -0,0 +1,14 @@
---
engines:
duplication:
enabled: false
fixme:
enabled: true
markdownlint:
enabled: true
ratings:
paths:
- "**.c"
- "**.h"
- "**.l"
- "**.md"

36
.travis.yml Normal file
View File

@ -0,0 +1,36 @@
sudo: required
dist: trusty
language: c
# download and install our required cross compilers
install:
# Make sure we can install i386 packages as some adtools binaries
# requires i386 libraries being installed to work in the 64bit env
# of Travis
- sudo dpkg --add-architecture i386
- sudo apt-get -qq update || true
- sudo apt-get -qq install libc6:i386
# Install all adtools related stuff we need
- curl -L https://dl.bintray.com/jens-maus/adtools/adtools-utils.tar.bz2 | sudo tar xj -C /
- if [[ ${BUILD} =~ os3|release ]]; then curl -L https://dl.bintray.com/jens-maus/adtools/adtools-m68k-amigaos.tar.bz2 | sudo tar xj -C / ; fi
- if [[ ${BUILD} =~ os4|release ]]; then curl -L https://dl.bintray.com/jens-maus/adtools/adtools-ppc-amigaos.tar.bz2 | sudo tar xj -C / ; fi
- if [[ ${BUILD} =~ mos|release ]]; then curl -L https://dl.bintray.com/jens-maus/adtools/adtools-ppc-morphos.tar.bz2 | sudo tar xj -C / ; fi
- if [[ ${BUILD} =~ aros-ppc|release ]]; then curl -L https://dl.bintray.com/jens-maus/adtools/adtools-ppc-aros.tar.bz2 | sudo tar xj -C / ; fi
- if [[ ${BUILD} =~ aros-i386|release ]]; then curl -L https://dl.bintray.com/jens-maus/adtools/adtools-i386-aros.tar.bz2 | sudo tar xj -C / ; fi
- if [[ ${BUILD} =~ aros-x86_64|release ]]; then curl -L https://dl.bintray.com/jens-maus/adtools/adtools-x86_64-aros.tar.bz2 | sudo tar xj -C / ; fi
- if [[ ${BUILD} =~ mingw32|release ]]; then sudo apt-get -qq install binutils-mingw-w64-i686 gcc-mingw-w64-i686 ; fi
# set the PATH variable to the directories the cross compilers are installed.
before_script:
- export PATH=/usr/local/amiga/bin:/opt/m68k-amigaos/bin:/opt/ppc-amigaos/bin:/opt/ppc-morphos/bin:${PATH}
# specify a list of variables to test (here we test the build for our supported
# list of operating systems).
env:
- BUILD="-f GNUmakefile.68k OS=os3"
- BUILD="-f GNUmakefile.os4 OS=os4"
# the build command to execute for each test
script:
- make -C library -j1 ${BUILD}

View File

@ -1,4 +1,9 @@
# An ISO 'C' (1994) compliant runtime library for the Amiga # clib2 An ISO 'C' (1994) compliant runtime library for AmigaOS
[![Build Status](https://travis-ci.org/adtools/clib2.svg?branch=master)](https://travis-ci.org/adtools/clib2)
[![Code Climate](https://codeclimate.com/github/adtools/clib2/badges/gpa.svg)](https://codeclimate.com/github/adtools/clib2)
[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Github Issues](http://githubbadges.herokuapp.com/adtools/clib2/issues.svg)](https://github.com/adtools/clib2/issues)
## What is this? ## What is this?

View File

@ -333,6 +333,8 @@ C_LIB = \
stdlib_getsp.o \ stdlib_getsp.o \
stdlib_get_errno.o \ stdlib_get_errno.o \
stdlib_get_slab_usage.o \ stdlib_get_slab_usage.o \
stdlib_get_slab_allocations.o \
stdlib_get_slab_stats.o \
stdlib_isresident.o \ stdlib_isresident.o \
stdlib_labs.o \ stdlib_labs.o \
stdlib_llabs.o \ stdlib_llabs.o \
@ -372,6 +374,7 @@ C_LIB = \
stdlib_showerror.o \ stdlib_showerror.o \
stdlib_slab.o \ stdlib_slab.o \
stdlib_slab_max_size.o \ stdlib_slab_max_size.o \
stdlib_slab_purge_threshold.o \
stdlib_srand.o \ stdlib_srand.o \
stdlib_stacksize.o \ stdlib_stacksize.o \
stdlib_stack_usage.o \ stdlib_stack_usage.o \
@ -1123,23 +1126,29 @@ $(LIBC_OBJS)/stdlib_getdefstacksize.o : stdlib_getdefstacksize.c stdlib_gcc_help
$(LIBC_OBJS)/stdlib_shell_escape.o : stdlib_shell_escape.c stdlib_gcc_help.h $(LIBC_OBJS)/stdlib_shell_escape.o : stdlib_shell_escape.c stdlib_gcc_help.h
$(LIBC_OBJS)/stdlib_alloca.o : stdlib_alloca.c stdlib_memory.h $(LIBC_OBJS)/stdlib_alloca.o : stdlib_alloca.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_calloc.o : stdlib_calloc.c stdlib_memory.h $(LIBC_OBJS)/stdlib_calloc.o : stdlib_calloc.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_free.o : stdlib_free.c stdlib_memory.h $(LIBC_OBJS)/stdlib_free.o : stdlib_free.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_malloc.o : stdlib_malloc.c stdlib_memory.h $(LIBC_OBJS)/stdlib_malloc.o : stdlib_malloc.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_slab.o : stdlib_slab.c stdlib_memory.h $(LIBC_OBJS)/stdlib_slab.o : stdlib_slab.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h $(LIBC_OBJS)/stdlib_slab_purge_threshold.o : stdlib_slab_purge_threshold.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h $(LIBC_OBJS)/stdlib_get_slab_stats.o : stdlib_get_slab_stats.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h $(LIBC_OBJS)/stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_red_black.o : stdlib_red_black.c stdlib_memory.h $(LIBC_OBJS)/stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_get_slab_allocations.o : stdlib_get_slab_allocations.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h include/stdlib.h
$(LIBC_OBJS)/stdlib_red_black.o : stdlib_red_black.c stdlib_memory.h include/stdlib.h
############################################################################## ##############################################################################

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "amiga.lib 1.209" #define VERS "amiga.lib 1.212"
#define VSTRING "amiga.lib 1.209 (21.11.2016)\r\n" #define VSTRING "amiga.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: amiga.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: amiga.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "c.lib 1.209" #define VERS "c.lib 1.212"
#define VSTRING "c.lib 1.209 (21.11.2016)\r\n" #define VSTRING "c.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: c.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: c.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -1,3 +1,67 @@
c.lib 1.212 (27.11.2016)
- Unused slabs which get recycled are no longer reinitialized from
scratch if their chunk size matches what the allocator needed.
If the chunk size matches, the list of available chunks is
left unchanged, and just the various counters are reset.
- Added __get_slab_stats() function.
- Added support for global __slab_purge_threshold tuning variable.
c.lib 1.211 (23.11.2016)
- Added more consistency checking to the slab allocator, which is
built if DEBUG is defined in "stdlib_slab.c".
- Memory allocations are no longer guaranteed to be aligned to
64 bit word boundaries. In fact, this has not even worked
reliably in the past 10 years.
- Memory allocation request sizes are now rounded to multiples of
32 bit words (the size of an address pointer) instead to the
size of a 64 bit word.
- Reduced the memory footprint of the memory allocation management
data structures by reusing the most significant bit of the
memory allocation size. This allows many more allocations to fit
into the 32 byte chunk slabs, but limits the maximum memory
allocation size to a little less than 2 GBytes.
- Added integer overflow checks to the memory management code.
- Reduced the memory management overhead further. This cuts an
additional 8 bytes per allocation, unless neither the slab
allocator nor memory pools are available. With this reduction
the slab allocator is able to use 16 byte chunks, which cover
memory allocation requests of 1..8 bytes.
- Fixed a bug caused by returning an allocation back to a slab
which passed the wrong pointer.
c.lib 1.210 (22.11.2016)
- Added __get_slab_allocations() function which will report information
about each memory allocation made by the slab allocator which does
not come from a slab.
- If the first slab in the list of slabs which share the same chunk
size has no more room, it means that all other slabs following
it have no room either. This speeds up the test to find a slab with
free space, which can now abort and directly proceed to allocate
memory for a new slab.
- If an empty slab's decay count hits zero, it is moved to the front
of the empty slab list to be reclaimed more quickly.
- Allocations made from the slab now carry a pointer back to the
slab which they are a part of. This speeds up deallocation but
has the downside of making the smallest usable slab chunk size
64 bytes, which is double what used to be the minimum before.
c.lib 1.209 (21.11.2016) c.lib 1.209 (21.11.2016)
- The maximum slab size is now 2^17 bytes (= 131072). If you request - The maximum slab size is now 2^17 bytes (= 131072). If you request

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "debug.lib 1.209" #define VERS "debug.lib 1.212"
#define VSTRING "debug.lib 1.209 (21.11.2016)\r\n" #define VSTRING "debug.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: debug.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: debug.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -173,6 +173,19 @@ extern int rand_r(unsigned int * seed);
extern unsigned long __slab_max_size; extern unsigned long __slab_max_size;
/*
* The slab allocator will periodically free all currently unused memory.
* You can control how much memory should be released, instead of
* releasing everything.
*
* This would make the slab allocator release only up to 512 KBytes of
* unused memory at a time:
*
* unsigned long __slab_purge_threshold = 512 * 1024;
*/
extern unsigned long __slab_purge_threshold;
/****************************************************************************/ /****************************************************************************/
/* /*
@ -181,7 +194,8 @@ extern unsigned long __slab_max_size;
* following function to do so. * following function to do so.
* *
* Please note that this function works within the context of the memory * Please note that this function works within the context of the memory
* allocation system and may not be safe to call from interrupt code. * allocation system and is not safe to call from interrupt code. It may
* break a Forbid() or Disable() condition.
*/ */
extern void __free_unused_slabs(void); extern void __free_unused_slabs(void);
@ -198,7 +212,8 @@ extern void __free_unused_slabs(void);
* not operational. * not operational.
* *
* Please note that this function works within the context of the memory * Please note that this function works within the context of the memory
* allocation system and may not be safe to call from interrupt code. * allocation system and is not safe to call from interrupt code. It may
* break a Forbid() or Disable() condition.
*/ */
/****************************************************************************/ /****************************************************************************/
@ -215,7 +230,8 @@ struct __slab_usage_information
size_t sui_num_single_allocations; size_t sui_num_single_allocations;
/* Total number of bytes allocated for memory not managed /* Total number of bytes allocated for memory not managed
* by slabs. * by slabs. This includes the management overhead for
* each allocation.
*/ */
size_t sui_total_single_allocation_size; size_t sui_total_single_allocation_size;
@ -247,6 +263,11 @@ struct __slab_usage_information
/* How many memory chunks in this slab are being used? */ /* How many memory chunks in this slab are being used? */
size_t sui_num_chunks_used; size_t sui_num_chunks_used;
/* How many time was this slab reused without reinitializing
* it all over again from scratch?
*/
size_t sui_num_reused;
}; };
/****************************************************************************/ /****************************************************************************/
@ -259,6 +280,93 @@ void __get_slab_usage(__slab_usage_callback callback);
/****************************************************************************/ /****************************************************************************/
/*
* You can obtain runtime statistics about the memory allocations
* which the slab allocator did not fit into slabs. This works
* just like __get_slab_usage() in that the callback function
* you provide will be called for each single allocation that
* is not part of a slab.
*
* Your callback function must return 0 if it wants to be called again,
* for the next slab, or return -1 to stop. Note that your callback
* function may not be called if the slab allocator did not
* allocate memory outside of slabs.
*
* Please note that this function works within the context of the memory
* allocation system and is not safe to call from interrupt code. It may
* break a Forbid() or Disable() condition.
*/
/* This is what your callback function will see when it is invoked. */
struct __slab_allocation_information
{
/* Number of allocations which are not managed by slabs, but
* are handled separate.
*/
size_t sai_num_single_allocations;
/* Total number of bytes allocated for memory not managed
* by slabs. This includes the management overhead for
* each allocation.
*/
size_t sai_total_single_allocation_size;
/*
* The following data is updated for each slab which
* your callback function sees.
*/
/* Index number of the allocation being reported (0 = no allocations
* outside of slabs are in use).
*/
int sai_allocation_index;
/* Size of this allocation, as requested by the program which
* called malloc(), realloc() or alloca().
*/
size_t sai_allocation_size;
/* Total size of this allocation, including management data
* structure overhead.
*/
size_t sai_total_allocation_size;
};
/****************************************************************************/
typedef int (*__slab_allocation_callback)(const struct __slab_allocation_information * sui);
/****************************************************************************/
void __get_slab_allocations(__slab_allocation_callback callback);
/****************************************************************************/
/*
* You can obtain information about the memory managed by the slab allocator,
* as well as additional information about the slab allocator's performance
* in JSON format. This format can be used for more detailed analysis.
*
* You supply a function which will be called for each line of the JSON
* data produced. You can store this data in a file, or in the clipboard,
* for later use. Your function must return 0 if it wants to be called
* again, or return -1 if it wants to stop (e.g. if an error occured
* when writing the JSON data to disk). The same "user_data" pointer which
* you pass to __get_slab_stats() will be passed to your callback function.
*
* Please note that this function works within the context of the memory
* allocation system and is not safe to call from interrupt code. It may
* break a Forbid() or Disable() condition.
*/
typedef int (* __slab_status_callback)(void * user_data, const char * line, size_t line_length);
/****************************************************************************/
extern void __get_slab_stats(void * user_data, __slab_status_callback callback);
/****************************************************************************/
/* /*
* You can request to use the alloca() variant that actually does allocate * You can request to use the alloca() variant that actually does allocate
* memory from the system rather than the current stack frame, which will * memory from the system rather than the current stack frame, which will

View File

@ -219,6 +219,8 @@ C_LIB := \
stdlib_getsp.o \ stdlib_getsp.o \
stdlib_get_errno.o \ stdlib_get_errno.o \
stdlib_get_slab_usage.o \ stdlib_get_slab_usage.o \
stdlib_get_slab_allocations.o \
stdlib_get_slab_stats.o \
stdlib_isresident.o \ stdlib_isresident.o \
stdlib_labs.o \ stdlib_labs.o \
stdlib_llabs.o \ stdlib_llabs.o \
@ -259,6 +261,7 @@ C_LIB := \
stdlib_showerror.o \ stdlib_showerror.o \
stdlib_slab.o \ stdlib_slab.o \
stdlib_slab_max_size.o \ stdlib_slab_max_size.o \
stdlib_slab_purge_threshold.o \
stdlib_srand.o \ stdlib_srand.o \
stdlib_stacksize.o \ stdlib_stacksize.o \
stdlib_stack_usage.o \ stdlib_stack_usage.o \

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "m.lib 1.209" #define VERS "m.lib 1.212"
#define VSTRING "m.lib 1.209 (21.11.2016)\r\n" #define VSTRING "m.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: m.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: m.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "m881.lib 1.209" #define VERS "m881.lib 1.212"
#define VSTRING "m881.lib 1.209 (21.11.2016)\r\n" #define VSTRING "m881.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: m881.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: m881.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "net.lib 1.209" #define VERS "net.lib 1.212"
#define VSTRING "net.lib 1.209 (21.11.2016)\r\n" #define VSTRING "net.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: net.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: net.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -520,6 +520,8 @@ STDLIB_OBJ = \
stdlib_getsp.o \ stdlib_getsp.o \
stdlib_get_errno.o \ stdlib_get_errno.o \
stdlib_get_slab_usage.o \ stdlib_get_slab_usage.o \
stdlib_get_slab_allocations.o \
stdlib_get_slab_stats.o \
stdlib_isresident.o \ stdlib_isresident.o \
stdlib_labs.o \ stdlib_labs.o \
stdlib_ldiv.o \ stdlib_ldiv.o \
@ -551,6 +553,7 @@ STDLIB_OBJ = \
stdlib_showerror.o \ stdlib_showerror.o \
stdlib_slab.o \ stdlib_slab.o \
stdlib_slab_max_size.o \ stdlib_slab_max_size.o \
stdlib_slab_purge_threshold.o \
stdlib_srand.o \ stdlib_srand.o \
stdlib_arg.o \ stdlib_arg.o \
stdlib_stack_usage.o \ stdlib_stack_usage.o \
@ -803,6 +806,10 @@ stdlib_slab.o : stdlib_slab.c stdlib_memory.h
stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h
stdlib_slab_max_size.o : stdlib_slab_max_size.c stdlib_memory.h
stdlib_slab_purge_threshold.o : stdlib_slab_purge_threshold.o stdlib_memory.h
stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h
stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h

View File

@ -31,6 +31,8 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*#define DEBUG*/
#ifndef _STDLIB_HEADERS_H #ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h" #include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */ #endif /* _STDLIB_HEADERS_H */
@ -165,7 +167,7 @@ dump_memory(unsigned char * m,int size,int ignore)
STATIC VOID STATIC VOID
check_memory_node(struct MemoryNode * mn,const char * file,int line) check_memory_node(struct MemoryNode * mn,const char * file,int line)
{ {
size_t size = mn->mn_Size; ULONG size = GET_MN_SIZE(mn);
unsigned char * head = (unsigned char *)(mn + 1); unsigned char * head = (unsigned char *)(mn + 1);
unsigned char * body = head + MALLOC_HEAD_SIZE; unsigned char * body = head + MALLOC_HEAD_SIZE;
unsigned char * tail = body + size; unsigned char * tail = body + size;
@ -227,10 +229,12 @@ check_memory_node(struct MemoryNode * mn,const char * file,int line)
if(mn->mn_AlreadyFree) if(mn->mn_AlreadyFree)
{ {
for(i = 0 ; i < size ; i++) ULONG j;
for(j = 0 ; j < size ; j++)
{ {
if(body[i] != MALLOC_FREE_FILL) if(body[j] != MALLOC_FREE_FILL)
max_body_damage = i+1; max_body_damage = j+1;
} }
if(max_body_damage > 0) if(max_body_damage > 0)
@ -345,17 +349,17 @@ remove_and_free_memory_node(struct MemoryNode * mn)
__memory_lock(); __memory_lock();
#if defined(__MEM_DEBUG)
{
Remove((struct Node *)mn); Remove((struct Node *)mn);
#if defined(__USE_MEM_TREES) && defined(__MEM_DEBUG) #if defined(__USE_MEM_TREES)
{ {
__red_black_tree_remove(&__memory_tree,mn); __red_black_tree_remove(&__memory_tree,mn);
} }
#endif /* __USE_MEM_TREES && __MEM_DEBUG */ #endif /* __USE_MEM_TREES */
#ifdef __MEM_DEBUG allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + GET_MN_SIZE(mn) + MALLOC_TAIL_SIZE;
{
allocation_size = sizeof(*mn) + MALLOC_HEAD_SIZE + mn->mn_Size + MALLOC_TAIL_SIZE;
assert( allocation_size == mn->mn_AllocationSize ); assert( allocation_size == mn->mn_AllocationSize );
@ -363,7 +367,7 @@ remove_and_free_memory_node(struct MemoryNode * mn)
} }
#else #else
{ {
allocation_size = sizeof(*mn) + mn->mn_Size; allocation_size = sizeof(*mn) + GET_MN_SIZE(mn);
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
@ -371,19 +375,57 @@ remove_and_free_memory_node(struct MemoryNode * mn)
{ {
/* Are we using the slab allocator? */ /* Are we using the slab allocator? */
if (__slab_data.sd_InUse) if (__slab_data.sd_InUse)
{
__slab_free(mn,allocation_size); __slab_free(mn,allocation_size);
}
else if (__memory_pool != NULL) else if (__memory_pool != NULL)
{
FreePooled(__memory_pool,mn,allocation_size); FreePooled(__memory_pool,mn,allocation_size);
}
else else
{
#if defined(__MEM_DEBUG)
{
FreeMem(mn,allocation_size); FreeMem(mn,allocation_size);
} }
#else #else
{
struct MinNode * mln = (struct MinNode *)mn;
mln--;
Remove((struct Node *)mln);
FreeMem(mln,sizeof(*mln) + allocation_size);
}
#endif /* __MEM_DEBUG */
}
}
#else
{ {
if (__memory_pool != NULL) if (__memory_pool != NULL)
{
FreePooled(__memory_pool,mn,allocation_size); FreePooled(__memory_pool,mn,allocation_size);
}
else else
{
#if defined(__MEM_DEBUG)
{
FreeMem(mn,allocation_size); FreeMem(mn,allocation_size);
} }
#else
{
struct MinNode * mln = (struct MinNode *)mn;
mln--;
Remove((struct Node *)mln);
FreeMem(mln,sizeof(*mln) + allocation_size);
}
#endif /* __MEM_DEBUG */
}
}
#endif /* __USE_SLAB_ALLOCATOR */ #endif /* __USE_SLAB_ALLOCATOR */
__current_memory_allocated -= allocation_size; __current_memory_allocated -= allocation_size;
@ -401,7 +443,7 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
size_t size = mn->mn_Size; ULONG size = GET_MN_SIZE(mn);
check_memory_node(mn,file,line); check_memory_node(mn,file,line);
@ -409,7 +451,7 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
{ {
#ifdef __MEM_DEBUG_LOG #ifdef __MEM_DEBUG_LOG
{ {
kprintf("[%s] - %10ld 0x%08lx [",__program_name,mn->mn_Size,mn->mn_Allocation); kprintf("[%s] - %10ld 0x%08lx [",__program_name,size,mn->mn_Allocation);
if(mn->mn_File != NULL) if(mn->mn_File != NULL)
kprintf("allocated at %s:%ld, ",mn->mn_File,mn->mn_Line); kprintf("allocated at %s:%ld, ",mn->mn_File,mn->mn_Line);
@ -436,14 +478,14 @@ __free_memory_node(struct MemoryNode * mn,const char * UNUSED file,int UNUSED li
{ {
#ifdef __MEM_DEBUG_LOG #ifdef __MEM_DEBUG_LOG
{ {
kprintf("[%s] - %10ld 0x%08lx [",__program_name,mn->mn_Size,mn->mn_Allocation); kprintf("[%s] - %10ld 0x%08lx [",__program_name,size,mn->mn_Allocation);
kprintf("FAILED]\n"); kprintf("FAILED]\n");
} }
#endif /* __MEM_DEBUG_LOG */ #endif /* __MEM_DEBUG_LOG */
kprintf("[%s] %s:%ld:Allocation at address 0x%08lx, size %ld", kprintf("[%s] %s:%ld:Allocation at address 0x%08lx, size %ld",
__program_name,file,line,mn->mn_Allocation,mn->mn_Size); __program_name,file,line,mn->mn_Allocation,size);
if(mn->mn_File != NULL) if(mn->mn_File != NULL)
kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line); kprintf(", allocated at %s:%ld",mn->mn_File,mn->mn_Line);
@ -467,6 +509,9 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
assert(ptr != NULL); assert(ptr != NULL);
SHOWPOINTER(ptr);
SHOWVALUE(force);
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
{ {
/*if((rand() % 16) == 0) /*if((rand() % 16) == 0)
@ -480,7 +525,7 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
{ {
if(mn != NULL) if(mn != NULL)
{ {
if(force || (NOT mn->mn_NeverFree)) if(force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE))
__free_memory_node(mn,file,line); __free_memory_node(mn,file,line);
} }
else else
@ -502,7 +547,9 @@ __free_memory(void * ptr,BOOL force,const char * file,int line)
{ {
assert( mn != NULL ); assert( mn != NULL );
if(mn != NULL && (force || (NOT mn->mn_NeverFree))) SHOWVALUE(mn->mn_Size);
if(mn != NULL && (force || FLAG_IS_CLEAR(mn->mn_Size, MN_SIZE_NEVERFREE)))
__free_memory_node(mn,file,line); __free_memory_node(mn,file,line);
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */

View File

@ -0,0 +1,82 @@
/*
* :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.
*/
#ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */
/****************************************************************************/
#ifndef _STDLIB_MEMORY_H
#include "stdlib_memory.h"
#endif /* _STDLIB_MEMORY_H */
/****************************************************************************/
void
__get_slab_allocations(__slab_allocation_callback callback)
{
if(__slab_data.sd_InUse)
{
struct __slab_allocation_information sai;
memset(&sai,0,sizeof(sai));
__memory_lock();
sai.sai_num_single_allocations = __slab_data.sd_NumSingleAllocations;
sai.sai_total_single_allocation_size = __slab_data.sd_TotalSingleAllocationSize;
if(__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL)
{
const struct SlabSingleAllocation * ssa;
for(ssa = (struct SlabSingleAllocation *)__slab_data.sd_SingleAllocations.mlh_Head ;
ssa->ssa_MinNode.mln_Succ != NULL ;
ssa = (struct SlabSingleAllocation *)ssa->ssa_MinNode.mln_Succ)
{
sai.sai_allocation_index++;
sai.sai_allocation_size = ssa->ssa_Size - sizeof(*ssa);
sai.sai_total_allocation_size = ssa->ssa_Size;
if((*callback)(&sai) != 0)
break;
}
}
else
{
(*callback)(&sai);
}
__memory_unlock();
}
}

View File

@ -0,0 +1,200 @@
/*
* :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.
*/
#ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */
/****************************************************************************/
#ifndef _STDLIB_MEMORY_H
#include "stdlib_memory.h"
#endif /* _STDLIB_MEMORY_H */
/****************************************************************************/
struct context
{
int status;
void * user_data;
__slab_status_callback callback;
char * buffer;
size_t buffer_size;
};
/****************************************************************************/
static void print(struct context * ct, const char * format, ...)
{
if(ct->status == 0)
{
va_list args;
int len;
va_start(args,format);
len = vsnprintf(ct->buffer, ct->buffer_size, format, args);
va_end(args);
/* This shouldn't happen: the buffer ought to be large enough
* to hold every single line.
*/
if(len >= (int)ct->buffer_size)
len = strlen(ct->buffer);
ct->status = (*ct->callback)(ct->user_data, ct->buffer, len);
}
}
/****************************************************************************/
void
__get_slab_stats(void * user_data, __slab_status_callback callback)
{
if(__slab_data.sd_InUse)
{
static int times_checked = 1;
const struct SlabNode * sn;
size_t num_empty_slabs = 0;
size_t num_full_slabs = 0;
size_t num_slabs = 0;
size_t slab_allocation_size = 0;
size_t total_slab_allocation_size = 0;
struct context ct;
char line[1024];
char time_buffer[40];
time_t now;
struct tm when;
int i;
memset(&ct, 0, sizeof(ct));
ct.user_data = user_data;
ct.callback = callback;
ct.buffer = line;
ct.buffer_size = sizeof(line);
__memory_lock();
now = time(NULL);
localtime_r(&now, &when);
strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S", &when);
print(&ct,"{\n");
print(&ct,"\t\"when\": \"%s\",\n", time_buffer);
print(&ct,"\t\"times_checked\": %d,\n", times_checked++);
print(&ct,"\t\"slab_size\": %zu,\n", __slab_data.sd_StandardSlabSize);
print(&ct,"\t\"num_single_allocations\": %zu,\n", __slab_data.sd_NumSingleAllocations);
print(&ct,"\t\"total_single_allocation_size\": %zu,\n", __slab_data.sd_TotalSingleAllocationSize);
if(__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL)
{
const struct SlabSingleAllocation * ssa;
print(&ct,"\t\"single_allocations\": [\n");
for(ssa = (struct SlabSingleAllocation *)__slab_data.sd_SingleAllocations.mlh_Head ;
ssa->ssa_MinNode.mln_Succ != NULL && ct.status == 0 ;
ssa = (struct SlabSingleAllocation *)ssa->ssa_MinNode.mln_Succ)
{
print(&ct,"\t\t{ \"size\": %lu, \"total_size\": %lu }%s\n",
ssa->ssa_Size - sizeof(*ssa), ssa->ssa_Size,
ssa->ssa_MinNode.mln_Succ->mln_Succ != NULL ? "," : "");
}
print(&ct,"\t],\n");
}
else
{
print(&ct,"\t\"single_allocations\": [],\n");
}
for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++)
{
for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
sn->sn_MinNode.mln_Succ != NULL ;
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
{
if (sn->sn_UseCount == 0)
num_empty_slabs++;
else if (sn->sn_UseCount == sn->sn_Count)
num_full_slabs++;
num_slabs++;
slab_allocation_size += sn->sn_ChunkSize * sn->sn_UseCount;
total_slab_allocation_size += sizeof(*sn) + __slab_data.sd_StandardSlabSize;
}
}
print(&ct,"\t\"num_slabs\": %zu,\n", num_slabs);
print(&ct,"\t\"num_empty_slabs\": %zu,\n", num_empty_slabs);
print(&ct,"\t\"num_full_slabs\": %zu,\n", num_full_slabs);
print(&ct,"\t\"slab_allocation_size\": %zu,\n", slab_allocation_size);
print(&ct,"\t\"total_slab_allocation_size\": %zu,\n", total_slab_allocation_size);
if(num_slabs > 0)
{
const char * eol = "";
print(&ct,"\t\"slabs\": [\n");
for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) && ct.status == 0 ; i++)
{
for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ;
sn->sn_MinNode.mln_Succ != NULL && ct.status == 0 ;
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ)
{
print(&ct,"%s\t\t{ \"size\": %lu, \"chunks\": %lu, \"chunks_in_use\": %lu, \"times_reused\": %lu }",
eol,
sn->sn_ChunkSize,
sn->sn_Count,
sn->sn_UseCount,
sn->sn_NumReused);
eol = ",\n";
}
}
print(&ct,"\n\t]\n");
}
else
{
print(&ct,"\t\"slabs\": []\n");
}
print(&ct,"}\n");
__memory_unlock();
}
}

View File

@ -47,7 +47,7 @@ __get_slab_usage(__slab_usage_callback callback)
if(__slab_data.sd_InUse) if(__slab_data.sd_InUse)
{ {
struct __slab_usage_information sui; struct __slab_usage_information sui;
struct SlabNode * sn; const struct SlabNode * sn;
BOOL stop; BOOL stop;
int i; int i;
@ -87,6 +87,7 @@ __get_slab_usage(__slab_usage_callback callback)
sui.sui_chunk_size = sn->sn_ChunkSize; sui.sui_chunk_size = sn->sn_ChunkSize;
sui.sui_num_chunks = sn->sn_Count; sui.sui_num_chunks = sn->sn_Count;
sui.sui_num_chunks_used = sn->sn_UseCount; sui.sui_num_chunks_used = sn->sn_UseCount;
sui.sui_num_reused = sn->sn_NumReused;
sui.sui_slab_index++; sui.sui_slab_index++;
@ -98,6 +99,10 @@ __get_slab_usage(__slab_usage_callback callback)
} }
} }
} }
else
{
(*callback)(&sui);
}
__memory_unlock(); __memory_unlock();
} }

View File

@ -31,6 +31,8 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*#define DEBUG*/
#ifndef _STDLIB_HEADERS_H #ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h" #include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */ #endif /* _STDLIB_HEADERS_H */
@ -72,25 +74,6 @@ struct MinList NOCOMMON __memory_list;
/****************************************************************************/ /****************************************************************************/
size_t
__get_allocation_size(size_t size)
{
#ifndef __MEM_DEBUG
{
size_t total_allocation_size;
total_allocation_size = sizeof(struct MemoryNode) + size;
/* Round up the allocation size to the physical allocation granularity. */
size += ((total_allocation_size + MEM_BLOCKMASK) & ~((ULONG)MEM_BLOCKMASK)) - total_allocation_size;
}
#endif /* __MEM_DEBUG */
return(size);
}
/****************************************************************************/
void * void *
__allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_name,int UNUSED debug_line_number) __allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_name,int UNUSED debug_line_number)
{ {
@ -135,13 +118,32 @@ __allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_nam
} }
#else #else
{ {
/* Round up the allocation size to the physical allocation granularity. */ /* Round up allocation to a multiple of 32 bits. */
size = __get_allocation_size(size); if((size & 3) != 0)
size += 4 - (size & 3);
allocation_size = sizeof(*mn) + size; allocation_size = sizeof(*mn) + size;
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
/* Integer overflow has occured? */
if(size == 0 || allocation_size < size)
{
__set_errno(ENOMEM);
goto out;
}
/* We reuse the MemoryNode.mn_Size field to mark
* allocations are not suitable for use with
* free() and realloc(). This limits allocation
* sizes to a little less than 2 GBytes.
*/
if(allocation_size & MN_SIZE_NEVERFREE)
{
__set_errno(ENOMEM);
goto out;
}
#if defined(__USE_SLAB_ALLOCATOR) #if defined(__USE_SLAB_ALLOCATOR)
{ {
/* Are we using the slab allocator? */ /* Are we using the slab allocator? */
@ -155,15 +157,27 @@ __allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_nam
} }
else else
{ {
#if defined(__amigaos4__) #ifdef __MEM_DEBUG
{
mn = AllocMem(allocation_size,MEMF_PRIVATE);
}
#else
{ {
mn = AllocMem(allocation_size,MEMF_ANY); mn = AllocMem(allocation_size,MEMF_ANY);
} }
#endif /* __amigaos4__ */ #else
{
struct MinNode * mln;
mln = AllocMem(sizeof(*mln) + allocation_size,MEMF_ANY);
if(mln != NULL)
{
AddTail((struct List *)&__memory_list,(struct Node *)mln);
mn = (struct MemoryNode *)&mln[1];
}
else
{
mn = NULL;
}
}
#endif /* __MEM_DEBUG */
} }
} }
#else #else
@ -174,15 +188,27 @@ __allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_nam
} }
else else
{ {
#if defined(__amigaos4__) #ifdef __MEM_DEBUG
{
mn = AllocMem(allocation_size,MEMF_PRIVATE);
}
#else
{ {
mn = AllocMem(allocation_size,MEMF_ANY); mn = AllocMem(allocation_size,MEMF_ANY);
} }
#endif /* __amigaos4__ */ #else
{
struct MinNode * mln;
mln = AllocMem(sizeof(*mln) + allocation_size,MEMF_ANY);
if(mln != NULL)
{
AddTail((struct List *)&__memory_list,(struct Node *)mln);
mn = (struct MemoryNode *)&mln[1];
}
else
{
mn = NULL;
}
}
#endif /* __MEM_DEBUG */
} }
} }
#endif /* __USE_SLAB_ALLOCATOR */ #endif /* __USE_SLAB_ALLOCATOR */
@ -194,9 +220,9 @@ __allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_nam
} }
mn->mn_Size = size; mn->mn_Size = size;
mn->mn_NeverFree = never_free;
AddTail((struct List *)&__memory_list,(struct Node *)mn); if(never_free)
SET_FLAG(mn->mn_Size, MN_SIZE_NEVERFREE);
__current_memory_allocated += allocation_size; __current_memory_allocated += allocation_size;
if(__maximum_memory_allocated < __current_memory_allocated) if(__maximum_memory_allocated < __current_memory_allocated)
@ -212,6 +238,8 @@ __allocate_memory(size_t size,BOOL never_free,const char * UNUSED debug_file_nam
char * body = head + MALLOC_HEAD_SIZE; char * body = head + MALLOC_HEAD_SIZE;
char * tail = body + size; char * tail = body + size;
AddTail((struct List *)&__memory_list,(struct Node *)mn);
mn->mn_AlreadyFree = FALSE; mn->mn_AlreadyFree = FALSE;
mn->mn_Allocation = body; mn->mn_Allocation = body;
mn->mn_AllocationSize = allocation_size; mn->mn_AllocationSize = allocation_size;
@ -475,7 +503,7 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init)
#if defined(__USE_SLAB_ALLOCATOR) #if defined(__USE_SLAB_ALLOCATOR)
{ {
/* ZZZ this is just for the purpose of testing */ /* ZZZ this is just for the purpose of testing */
#if 0 #if 1
{ {
TEXT slab_size_var[20]; TEXT slab_size_var[20];

View File

@ -124,7 +124,6 @@
#define __find_memory_node __find_memory_node_debug #define __find_memory_node __find_memory_node_debug
#define __free_memory_node __free_memory_node_debug #define __free_memory_node __free_memory_node_debug
#define __get_allocation_size __get_allocation_size_debug
#define __allocate_memory __allocate_memory_debug #define __allocate_memory __allocate_memory_debug
#define __memory_pool __memory_pool_debug #define __memory_pool __memory_pool_debug
@ -151,16 +150,24 @@ extern char * __getcwd(char * buffer,size_t buffer_size,const char *file,int lin
/****************************************************************************/ /****************************************************************************/
/* If this flag is set in mn_Size, then this memory allocation
* cannot be released with free() or used with realloc(). This
* flag is set by alloca().
*/
#define MN_SIZE_NEVERFREE (0x80000000UL)
/* This obtains the allocation size from a memory node, ignoring
* the "never free" flag altogether.
*/
#define GET_MN_SIZE(mn) ((mn)->mn_Size & ~MN_SIZE_NEVERFREE)
struct MemoryNode struct MemoryNode
{ {
struct MinNode mn_MinNode;
size_t mn_Size;
UBYTE mn_NeverFree;
#ifdef __MEM_DEBUG #ifdef __MEM_DEBUG
struct MinNode mn_MinNode;
UBYTE mn_AlreadyFree; UBYTE mn_AlreadyFree;
UBYTE mn_Pad0[2]; UBYTE mn_Pad0[3];
void * mn_Allocation; void * mn_Allocation;
size_t mn_AllocationSize; size_t mn_AllocationSize;
@ -179,9 +186,9 @@ struct MemoryNode
UBYTE mn_Pad1[3]; UBYTE mn_Pad1[3];
#endif /* __USE_MEM_TREES */ #endif /* __USE_MEM_TREES */
#else
UBYTE mn_Pad0[3];
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
ULONG mn_Size;
}; };
#ifdef __USE_MEM_TREES #ifdef __USE_MEM_TREES
@ -228,12 +235,26 @@ struct SlabNode
/* How many chunks of this slab are currently in use? */ /* How many chunks of this slab are currently in use? */
ULONG sn_UseCount; ULONG sn_UseCount;
/* How many times was this slab reused instead of allocating
* it from system memory?
*/
ULONG sn_NumReused;
/* This contains all the chunks of memory which are available /* This contains all the chunks of memory which are available
* for allocation. * for allocation.
*/ */
struct MinList sn_FreeList; struct MinList sn_FreeList;
}; };
/* Memory allocations which are not part of a slab are
* tracked using this data structure.
*/
struct SlabSingleAllocation
{
struct MinNode ssa_MinNode;
ULONG ssa_Size;
};
/* This is the global bookkeeping information for managing /* This is the global bookkeeping information for managing
* memory allocations from the slab data structure. * memory allocations from the slab data structure.
*/ */
@ -270,7 +291,7 @@ struct SlabData
*/ */
size_t sd_StandardSlabSize; size_t sd_StandardSlabSize;
/* These fields kees track of how many entries there are in /* These fields keep track of how many entries there are in
* the sd_SingleAllocations list, and how much memory these * the sd_SingleAllocations list, and how much memory these
* allocations occupy. * allocations occupy.
*/ */
@ -287,6 +308,7 @@ struct SlabData
extern struct SlabData NOCOMMON __slab_data; extern struct SlabData NOCOMMON __slab_data;
extern unsigned long NOCOMMON __slab_max_size; extern unsigned long NOCOMMON __slab_max_size;
extern unsigned long NOCOMMON __slab_purge_threshold;
/****************************************************************************/ /****************************************************************************/

View File

@ -31,6 +31,8 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*#define DEBUG*/
#ifndef _STDLIB_HEADERS_H #ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h" #include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */ #endif /* _STDLIB_HEADERS_H */
@ -76,6 +78,7 @@ __realloc(void *ptr,size_t size,const char * file,int line)
#endif /* UNIX_PATH_SEMANTICS */ #endif /* UNIX_PATH_SEMANTICS */
else else
{ {
size_t old_size;
struct MemoryNode * mn; struct MemoryNode * mn;
BOOL reallocate; BOOL reallocate;
@ -108,29 +111,23 @@ __realloc(void *ptr,size_t size,const char * file,int line)
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
if(mn == NULL || mn->mn_NeverFree) if(mn == NULL || FLAG_IS_SET(mn->mn_Size, MN_SIZE_NEVERFREE))
{ {
SHOWMSG("cannot free this chunk"); SHOWMSG("cannot free this chunk");
goto out; goto out;
} }
old_size = GET_MN_SIZE(mn);
/* Don't do anything unless the size of the allocation /* Don't do anything unless the size of the allocation
has really changed. */ has really changed. */
#if defined(__MEM_DEBUG) #if defined(__MEM_DEBUG)
{ {
reallocate = (mn->mn_Size != size); reallocate = (old_size != size);
} }
#else #else
{ {
size_t rounded_allocation_size; if(size > old_size)
/* Round the total allocation size to the operating system
granularity. */
rounded_allocation_size = __get_allocation_size(size);
assert( rounded_allocation_size >= size );
if(rounded_allocation_size > mn->mn_Size)
{ {
/* Allocation size should grow. */ /* Allocation size should grow. */
reallocate = TRUE; reallocate = TRUE;
@ -143,7 +140,7 @@ __realloc(void *ptr,size_t size,const char * file,int line)
allocation. We also take into account that the allocation. We also take into account that the
actual size of the allocation is affected by a actual size of the allocation is affected by a
certain operating system imposed granularity. */ certain operating system imposed granularity. */
reallocate = (rounded_allocation_size < mn->mn_Size && rounded_allocation_size <= mn->mn_Size / 2); reallocate = (size < old_size && size <= old_size / 2);
} }
} }
#endif /* __MEM_DEBUG */ #endif /* __MEM_DEBUG */
@ -152,7 +149,7 @@ __realloc(void *ptr,size_t size,const char * file,int line)
{ {
void * new_ptr; void * new_ptr;
D(("realloc() size has changed; old=%ld, new=%ld",mn->mn_Size,size)); D(("realloc() size has changed; old=%ld, new=%ld",old_size,size));
/* We allocate the new memory chunk before we /* We allocate the new memory chunk before we
attempt to replace the old. */ attempt to replace the old. */
@ -164,8 +161,8 @@ __realloc(void *ptr,size_t size,const char * file,int line)
} }
/* Copy the contents of the old allocation to the new buffer. */ /* Copy the contents of the old allocation to the new buffer. */
if(size > mn->mn_Size) if(size > old_size)
size = mn->mn_Size; size = old_size;
memmove(new_ptr,ptr,size); memmove(new_ptr,ptr,size);
@ -177,7 +174,7 @@ __realloc(void *ptr,size_t size,const char * file,int line)
} }
else else
{ {
D(("size didn't actually change that much (%ld -> %ld); returning memory block as is.",mn->mn_Size,size)); D(("size didn't actually change that much (%ld -> %ld); returning memory block as is.",old_size,size));
/* No change in size. */ /* No change in size. */
result = ptr; result = ptr;

View File

@ -47,46 +47,72 @@ struct SlabData NOCOMMON __slab_data;
/****************************************************************************/ /****************************************************************************/
struct SlabChunk
{
struct SlabNode * sc_Parent;
};
/****************************************************************************/
void * void *
__slab_allocate(size_t allocation_size) __slab_allocate(size_t allocation_size)
{ {
struct SlabChunk * chunk;
void * allocation = NULL; void * allocation = NULL;
size_t allocation_size_with_chunk_header;
D(("allocating %lu bytes of memory",allocation_size)); D(("allocating %lu bytes of memory",allocation_size));
assert( __slab_data.sd_StandardSlabSize > 0 ); assert( __slab_data.sd_StandardSlabSize > 0 );
/* Check for integer overflow. */
allocation_size_with_chunk_header = sizeof(*chunk) + allocation_size;
if(allocation_size_with_chunk_header < allocation_size)
return(NULL);
/* Number of bytes to allocate exceeds the slab size? /* Number of bytes to allocate exceeds the slab size?
* If so, allocate this memory chunk separately and * If so, allocate this memory chunk separately and
* keep track of it. * keep track of it.
*/ */
if(allocation_size > __slab_data.sd_StandardSlabSize) if(allocation_size_with_chunk_header > __slab_data.sd_StandardSlabSize)
{ {
struct MinNode * single_allocation; struct SlabSingleAllocation * ssa;
ULONG total_single_allocation_size = sizeof(*ssa) + allocation_size;
D(("allocation size is > %ld; this will be stored separately",__slab_data.sd_StandardSlabSize)); D(("allocation size is > %ld; this will be stored separately",__slab_data.sd_StandardSlabSize));
D(("allocating %ld (MinNode) + %ld = %ld bytes",sizeof(*single_allocation),allocation_size,sizeof(*single_allocation) + allocation_size)); D(("allocating %ld (MinNode+Size) + %ld = %ld bytes",sizeof(*ssa),allocation_size,total_single_allocation_size));
/* No integer overflow? */
if(allocation_size < total_single_allocation_size)
{
#if defined(__amigaos4__) #if defined(__amigaos4__)
{ {
single_allocation = AllocVec(sizeof(*single_allocation) + allocation_size,MEMF_PRIVATE); ssa = AllocMem(total_single_allocation_size,MEMF_PRIVATE);
} }
#else #else
{ {
single_allocation = AllocVec(sizeof(*single_allocation) + allocation_size,MEMF_ANY); ssa = AllocMem(total_single_allocation_size,MEMF_ANY);
} }
#endif /* __amigaos4__ */ #endif /* __amigaos4__ */
}
if(single_allocation != NULL) /* Integer overflow has occured. */
else
{ {
ssa = NULL;
}
if(ssa != NULL)
{
ssa->ssa_Size = total_single_allocation_size;
allocation = &ssa[1];
D(("single allocation = 0x%08lx",allocation)); D(("single allocation = 0x%08lx",allocation));
AddTail((struct List *)&__slab_data.sd_SingleAllocations,(struct Node *)single_allocation); AddTail((struct List *)&__slab_data.sd_SingleAllocations,(struct Node *)ssa);
__slab_data.sd_NumSingleAllocations++; __slab_data.sd_NumSingleAllocations++;
__slab_data.sd_TotalSingleAllocationSize += sizeof(*single_allocation) + allocation_size; __slab_data.sd_TotalSingleAllocationSize += total_single_allocation_size;
allocation = &single_allocation[1];
D(("single allocation succeeded at 0x%08lx (number of single allocations = %lu)", allocation, __slab_data.sd_NumSingleAllocations)); D(("single allocation succeeded at 0x%08lx (number of single allocations = %lu)", allocation, __slab_data.sd_NumSingleAllocations));
} }
@ -99,20 +125,27 @@ __slab_allocate(size_t allocation_size)
else else
{ {
struct MinList * slab_list = NULL; struct MinList * slab_list = NULL;
BOOL slab_reused = FALSE;
ULONG entry_size; ULONG entry_size;
ULONG chunk_size; ULONG chunk_size;
int slab_index; int slab_index;
D(("allocation size is <= %ld; this will be allocated from a slab",__slab_data.sd_StandardSlabSize)); D(("allocation size is <= %ld; this will be allocated from a slab",__slab_data.sd_StandardSlabSize));
/* Add room for a pointer back to the slab which
* the chunk belongs to.
*/
entry_size = sizeof(*chunk) + allocation_size;
/* Chunks must be at least as small as a MinNode, because /* Chunks must be at least as small as a MinNode, because
* that's what we use for keeping track of the chunks which * that's what we use for keeping track of the chunks which
* are available for allocation within each slab. * are available for allocation within each slab.
*/ */
entry_size = allocation_size;
if(entry_size < sizeof(struct MinNode)) if(entry_size < sizeof(struct MinNode))
entry_size = sizeof(struct MinNode); entry_size = sizeof(struct MinNode);
D(("final entry size prior to picking slab size = %ld bytes",entry_size));
/* Find a slab which keeps track of chunks that are no /* Find a slab which keeps track of chunks that are no
* larger than the amount of memory which needs to be * larger than the amount of memory which needs to be
* allocated. We end up picking the smallest chunk * allocated. We end up picking the smallest chunk
@ -139,18 +172,28 @@ __slab_allocate(size_t allocation_size)
SHOWVALUE(chunk_size); SHOWVALUE(chunk_size);
/* Find the first slab which has a free chunk and use it. */ /* The slab list is organized in such a way that the first
for(sn = (struct SlabNode *)slab_list->mlh_Head ; * entry always has a free chunk ready for allocation. If
sn->sn_MinNode.mln_Succ != NULL ; * there is no such free chunk, it means that no other
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) * slab nodes in this list have any free chunks.
*/
sn = (struct SlabNode *)slab_list->mlh_Head;
/* Make sure that the slab list is not empty. */
if(sn->sn_MinNode.mln_Succ != NULL)
{ {
D(("slab = 0x%08lx, chunk size = %ld", sn, sn->sn_ChunkSize)); D(("slab = 0x%08lx, chunk size = %ld", sn, sn->sn_ChunkSize));
assert( sn->sn_ChunkSize == chunk_size ); assert( sn->sn_ChunkSize == chunk_size );
allocation = (struct MemoryNode *)RemHead((struct List *)&sn->sn_FreeList); chunk = (struct SlabChunk *)RemHead((struct List *)&sn->sn_FreeList);
if(allocation != NULL) if(chunk != NULL)
{ {
/* Keep track of this chunk's parent slab. */
chunk->sc_Parent = sn;
allocation = &chunk[1];
D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)",allocation,sn,sn->sn_UseCount)); D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)",allocation,sn,sn->sn_UseCount));
/* Was this slab empty before we began using it again? */ /* Was this slab empty before we began using it again? */
@ -158,9 +201,8 @@ __slab_allocate(size_t allocation_size)
{ {
D(("slab is no longer empty")); D(("slab is no longer empty"));
/* Mark it as no longer empty. */ /* Pull it out of the list of slabs available for reuse. */
Remove((struct Node *)&sn->sn_EmptyLink); Remove((struct Node *)&sn->sn_EmptyLink);
sn->sn_EmptyDecay = 0;
} }
sn->sn_UseCount++; sn->sn_UseCount++;
@ -178,8 +220,6 @@ __slab_allocate(size_t allocation_size)
Remove((struct Node *)sn); Remove((struct Node *)sn);
AddTail((struct List *)slab_list, (struct Node *)sn); AddTail((struct List *)slab_list, (struct Node *)sn);
} }
break;
} }
} }
@ -213,10 +253,22 @@ __slab_allocate(size_t allocation_size)
/* Unlink from list of empty slabs. */ /* Unlink from list of empty slabs. */
Remove((struct Node *)free_node); Remove((struct Node *)free_node);
/* If the chunk size of the reused slab matches
* exactly what we need then we won't have to
* completely reinitialize it again.
*/
if(sn->sn_ChunkSize == chunk_size)
{
slab_reused = TRUE;
}
else
{
/* Unlink from list of slabs which keep chunks /* Unlink from list of slabs which keep chunks
* of the same size. * of the same size. It will be added there
* again, at a different position.
*/ */
Remove((struct Node *)sn); Remove((struct Node *)sn);
}
D(("reusing a slab")); D(("reusing a slab"));
@ -230,39 +282,53 @@ __slab_allocate(size_t allocation_size)
*/ */
if(new_sn == NULL) if(new_sn == NULL)
{ {
D(("no slab is available for reuse; allocating a new slab (%lu bytes)",sizeof(*sn) + __slab_data.sd_StandardSlabSize)); D(("no slab is available for reuse; allocating a new slab (%lu bytes)",sizeof(*new_sn) + __slab_data.sd_StandardSlabSize));
#if defined(__amigaos4__) #if defined(__amigaos4__)
{ {
new_sn = (struct SlabNode *)AllocVec(sizeof(*sn) + __slab_data.sd_StandardSlabSize,MEMF_PRIVATE); new_sn = (struct SlabNode *)AllocVec(sizeof(*new_sn) + __slab_data.sd_StandardSlabSize,MEMF_PRIVATE);
} }
#else #else
{ {
new_sn = (struct SlabNode *)AllocVec(sizeof(*sn) + __slab_data.sd_StandardSlabSize,MEMF_ANY); new_sn = (struct SlabNode *)AllocVec(sizeof(*new_sn) + __slab_data.sd_StandardSlabSize,MEMF_ANY);
} }
#endif /* __amigaos4__ */ #endif /* __amigaos4__ */
if(new_sn == NULL) if(new_sn == NULL)
D(("slab allocation failed")); D(("slab allocation failed"));
/* If this allocation went well, try to free all currently unused
* slabs which are ready for purging. This is done so that we don't
* keep allocating new memory all the time without cutting back on
* unused slabs.
*/
purge = TRUE; purge = TRUE;
} }
if(new_sn != NULL) if(new_sn != NULL)
{ {
struct MinNode * free_chunk;
ULONG num_free_chunks = 0;
BYTE * first_byte;
BYTE * last_byte;
D(("setting up slab 0x%08lx", new_sn)); D(("setting up slab 0x%08lx", new_sn));
assert( chunk_size <= __slab_data.sd_StandardSlabSize ); assert( chunk_size <= __slab_data.sd_StandardSlabSize );
/* Do we have to completely initialize this slab from scratch? */
if(NOT slab_reused)
{
struct SlabChunk * free_chunk;
ULONG num_free_chunks = 0;
BYTE * first_byte;
BYTE * last_byte;
memset(new_sn,0,sizeof(*new_sn)); memset(new_sn,0,sizeof(*new_sn));
NewList((struct List *)&new_sn->sn_FreeList); NewList((struct List *)&new_sn->sn_FreeList);
/* This slab has room for new allocations, so make sure that
* it goes to the front of the slab list. It will be used
* by the next allocation request of this size.
*/
AddHead((struct List *)slab_list,(struct Node *)new_sn);
/* Split up the slab memory into individual chunks /* Split up the slab memory into individual chunks
* of the same size and keep track of them * of the same size and keep track of them
* in the free list. The memory managed by * in the free list. The memory managed by
@ -272,32 +338,44 @@ __slab_allocate(size_t allocation_size)
first_byte = (BYTE *)&new_sn[1]; first_byte = (BYTE *)&new_sn[1];
last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size];
for(free_chunk = (struct MinNode *)first_byte ; for(free_chunk = (struct SlabChunk *)first_byte ;
free_chunk <= (struct MinNode *)last_byte; free_chunk <= (struct SlabChunk *)last_byte;
free_chunk = (struct MinNode *)(((BYTE *)free_chunk) + chunk_size)) free_chunk = (struct SlabChunk *)(((BYTE *)free_chunk) + chunk_size))
{ {
AddTail((struct List *)&new_sn->sn_FreeList, (struct Node *)free_chunk); AddTail((struct List *)&new_sn->sn_FreeList, (struct Node *)free_chunk);
num_free_chunks++; num_free_chunks++;
} }
D(("slab contains %lu chunks, %lu bytes each",num_free_chunks,chunk_size));
/* Grab the first free chunk (there has to be one). */
allocation = (struct MemoryNode *)RemHead((struct List *)&new_sn->sn_FreeList);
D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)",allocation,new_sn,new_sn->sn_UseCount+1));
assert( allocation != NULL );
/* Set up the new slab and put it where it belongs. */
new_sn->sn_EmptyDecay = 0;
new_sn->sn_UseCount = 1;
new_sn->sn_Count = num_free_chunks; new_sn->sn_Count = num_free_chunks;
new_sn->sn_ChunkSize = chunk_size; new_sn->sn_ChunkSize = chunk_size;
SHOWVALUE(new_sn->sn_ChunkSize); D(("new slab contains %lu chunks, %lu bytes each",num_free_chunks,chunk_size));
}
/* This slab was reused and need not be reinitialized from scratch. */
else
{
new_sn->sn_NumReused++;
AddHead((struct List *)slab_list,(struct Node *)new_sn); assert( new_sn->sn_FreeList.mlh_Head != NULL );
assert( new_sn->sn_ChunkSize == chunk_size );
assert( new_sn->sn_Count == 0 );
}
/* Grab the first free chunk (there has to be one). */
chunk = (struct SlabChunk *)RemHead((struct List *)&new_sn->sn_FreeList);
/* Keep track of this chunk's parent slab. */
chunk->sc_Parent = new_sn;
assert( chunk != NULL );
assert( chunk->sc_Parent == new_sn );
allocation = &chunk[1];
/* This slab is now in use. */
new_sn->sn_UseCount = 1;
D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)",allocation,new_sn,new_sn->sn_UseCount));
} }
/* Mark unused slabs for purging, and purge those which /* Mark unused slabs for purging, and purge those which
@ -305,6 +383,8 @@ __slab_allocate(size_t allocation_size)
*/ */
if(purge) if(purge)
{ {
size_t total_purged = 0;
D(("purging empty slabs")); D(("purging empty slabs"));
for(free_node = (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head ; for(free_node = (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head ;
@ -330,11 +410,33 @@ __slab_allocate(size_t allocation_size)
Remove((struct Node *)sn); Remove((struct Node *)sn);
FreeVec(sn); FreeVec(sn);
total_purged += sizeof(*sn) + __slab_data.sd_StandardSlabSize;
/* Stop releasing memory if we reach the threshold. If no
* threshold has been set, we will free as much memory
* as possible.
*/
if(__slab_purge_threshold > 0 && total_purged >= __slab_purge_threshold)
break;
} }
/* Give it another chance. */ /* Give it another chance. */
else else
{ {
sn->sn_EmptyDecay--; sn->sn_EmptyDecay--;
/* Is this slab ready for reuse now? */
if(sn->sn_EmptyDecay == 0)
{
/* Move it to the front of the list, so that
* it will be collected as soon as possible.
*/
if(free_node != (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head)
{
Remove((struct Node *)free_node);
AddHead((struct List *)&__slab_data.sd_EmptySlabs,(struct Node *)free_node);
}
}
} }
} }
} }
@ -354,6 +456,8 @@ __slab_allocate(size_t allocation_size)
void void
__slab_free(void * address,size_t allocation_size) __slab_free(void * address,size_t allocation_size)
{ {
struct SlabChunk * chunk;
D(("freeing allocation at 0x%08lx, %lu bytes",address,allocation_size)); D(("freeing allocation at 0x%08lx, %lu bytes",address,allocation_size));
assert( __slab_data.sd_StandardSlabSize > 0 ); assert( __slab_data.sd_StandardSlabSize > 0 );
@ -361,23 +465,54 @@ __slab_free(void * address,size_t allocation_size)
/* Number of bytes allocated exceeds the slab size? /* Number of bytes allocated exceeds the slab size?
* Then the chunk was allocated separately. * Then the chunk was allocated separately.
*/ */
if(allocation_size > __slab_data.sd_StandardSlabSize) if(sizeof(*chunk) + allocation_size > __slab_data.sd_StandardSlabSize)
{ {
struct MinNode * mn = address; struct SlabSingleAllocation * ssa = address;
ULONG size;
D(("allocation size is > %ld; this was stored separately",__slab_data.sd_StandardSlabSize)); D(("allocation size is > %ld; this was stored separately",__slab_data.sd_StandardSlabSize));
Remove((struct Node *)&mn[-1]);
FreeVec(&mn[-1]);
assert( __slab_data.sd_NumSingleAllocations > 0 ); assert( __slab_data.sd_NumSingleAllocations > 0 );
/* Management information (MinNode linkage, size in bytes) precedes
* the address returned by malloc(), etc.
*/
ssa--;
/* Verify that the allocation is really on the list we
* will remove it from.
*/
#if DEBUG
{
struct MinNode * mln;
BOOL found_allocation_in_list = FALSE;
for(mln = __slab_data.sd_SingleAllocations.mlh_Head ;
mln->mln_Succ != NULL ;
mln = mln->mln_Succ)
{
if(mln == (struct MinNode *)ssa)
{
found_allocation_in_list = TRUE;
break;
}
}
assert( found_allocation_in_list );
}
#endif /* DEBUG */
size = ssa->ssa_Size;
assert( size > 0 );
assert( sizeof(*ssa) + allocation_size == size );
assert( size <= __slab_data.sd_TotalSingleAllocationSize );
Remove((struct Node *)ssa);
FreeMem(ssa, size);
__slab_data.sd_NumSingleAllocations--; __slab_data.sd_NumSingleAllocations--;
__slab_data.sd_TotalSingleAllocationSize -= size;
assert( __slab_data.sd_TotalSingleAllocationSize <= sizeof(*mn) + allocation_size );
__slab_data.sd_TotalSingleAllocationSize -= sizeof(*mn) + allocation_size;
D(("number of single allocations = %ld", __slab_data.sd_NumSingleAllocations)); D(("number of single allocations = %ld", __slab_data.sd_NumSingleAllocations));
} }
@ -391,11 +526,15 @@ __slab_free(void * address,size_t allocation_size)
D(("allocation size is <= %ld; this was allocated from a slab",__slab_data.sd_StandardSlabSize)); D(("allocation size is <= %ld; this was allocated from a slab",__slab_data.sd_StandardSlabSize));
/* Add room for a pointer back to the slab which
* the chunk belongs to.
*/
entry_size = sizeof(*chunk) + allocation_size;
/* Chunks must be at least as small as a MinNode, because /* Chunks must be at least as small as a MinNode, because
* that's what we use for keeping track of the chunks which * that's what we use for keeping track of the chunks which
* are available for allocation within each slab. * are available for allocation within each slab.
*/ */
entry_size = allocation_size;
if(entry_size < sizeof(struct MinNode)) if(entry_size < sizeof(struct MinNode))
entry_size = sizeof(struct MinNode); entry_size = sizeof(struct MinNode);
@ -419,34 +558,95 @@ __slab_free(void * address,size_t allocation_size)
} }
} }
/* Find the slab which contains the memory chunk. */ /* Pick the slab which contains the memory chunk. */
if(slab_list != NULL) if(slab_list != NULL)
{ {
const size_t usable_range = __slab_data.sd_StandardSlabSize - chunk_size;
struct SlabNode * sn; struct SlabNode * sn;
BYTE * first_byte;
BYTE * last_byte;
BOOL freed = FALSE;
assert( chunk_size <= __slab_data.sd_StandardSlabSize ); assert( chunk_size <= __slab_data.sd_StandardSlabSize );
for(sn = (struct SlabNode *)slab_list->mlh_Head ; /* The pointer back to the slab which this chunk belongs
sn->sn_MinNode.mln_Succ != NULL ; * to precedes the address which __slab_allocate()
sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) * returned.
*/
chunk = address;
chunk--;
sn = chunk->sc_Parent;
#if DEBUG
{ {
struct SlabNode * other_sn;
BOOL slab_found = FALSE;
BOOL chunk_found = FALSE;
for(other_sn = (struct SlabNode *)slab_list->mlh_Head ;
other_sn->sn_MinNode.mln_Succ != NULL ;
other_sn = (struct SlabNode *)other_sn->sn_MinNode.mln_Succ)
{
if(other_sn == sn)
{
slab_found = TRUE;
break;
}
}
assert( slab_found );
if(slab_found)
{
struct MinNode * free_chunk;
BYTE * first_byte;
BYTE * last_byte;
first_byte = (BYTE *)&sn[1];
last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size];
for(free_chunk = (struct MinNode *)first_byte ;
free_chunk <= (struct MinNode *)last_byte;
free_chunk = (struct MinNode *)(((BYTE *)free_chunk) + chunk_size))
{
if(free_chunk == (struct MinNode *)chunk)
{
chunk_found = TRUE;
break;
}
}
}
assert( chunk_found );
}
#endif /* DEBUG */
SHOWVALUE(sn->sn_ChunkSize); SHOWVALUE(sn->sn_ChunkSize);
assert( sn->sn_ChunkSize != 0 );
assert( sn->sn_ChunkSize == chunk_size ); assert( sn->sn_ChunkSize == chunk_size );
first_byte = (BYTE *)&sn[1];
last_byte = &first_byte[usable_range];
/* Is this where the chunk belongs? */
if(first_byte <= (BYTE *)address && (BYTE *)address <= last_byte)
{
D(("allocation is part of slab 0x%08lx (slab use count = %ld)",sn,sn->sn_UseCount)); D(("allocation is part of slab 0x%08lx (slab use count = %ld)",sn,sn->sn_UseCount));
AddTail((struct List *)&sn->sn_FreeList, (struct Node *)address); #if DEBUG
{
struct MinNode * mln;
BOOL chunk_already_free = FALSE;
for(mln = sn->sn_FreeList.mlh_Head ;
mln->mln_Succ != NULL ;
mln = mln->mln_Succ)
{
if(mln == (struct MinNode *)chunk)
{
chunk_already_free = TRUE;
break;
}
}
assert( NOT chunk_already_free );
}
#endif /* DEBUG */
AddHead((struct List *)&sn->sn_FreeList, (struct Node *)chunk);
assert( sn->sn_UseCount > 0 ); assert( sn->sn_UseCount > 0 );
@ -474,14 +674,6 @@ __slab_free(void * address,size_t allocation_size)
Remove((struct Node *)sn); Remove((struct Node *)sn);
AddHead((struct List *)slab_list, (struct Node *)sn); AddHead((struct List *)slab_list, (struct Node *)sn);
} }
freed = TRUE;
break;
}
}
if(!freed)
D(("allocation at 0x%08lx could not be freed",address));
} }
else else
{ {
@ -547,6 +739,21 @@ __slab_init(size_t slab_size)
/****************************************************************************/ /****************************************************************************/
#if DEBUG
static int print_json(void * ignore,const char * buffer,size_t len)
{
extern void kputs(const char * str);
kputs(buffer);
return(0);
}
#endif /* DEBUG */
/****************************************************************************/
void void
__slab_exit(void) __slab_exit(void)
{ {
@ -554,11 +761,24 @@ __slab_exit(void)
if(__slab_data.sd_InUse) if(__slab_data.sd_InUse)
{ {
struct SlabSingleAllocation * ssa;
struct SlabNode * sn; struct SlabNode * sn;
struct SlabNode * sn_next; struct SlabNode * sn_next;
struct MinNode * mn; struct MinNode * mn;
struct MinNode * mn_next; struct MinNode * mn_next;
int i; size_t slab_count = 0, total_slab_size = 0;
size_t single_allocation_count = 0, total_single_allocation_size = 0;
int i, j;
#if DEBUG
{
kprintf("---BEGIN JSON DATA ---\n");
__get_slab_stats(NULL, print_json);
kprintf("---END JSON DATA ---\n\n");
}
#endif /* DEBUG */
D(("freeing slabs")); D(("freeing slabs"));
@ -566,33 +786,56 @@ __slab_exit(void)
for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++) for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++)
{ {
if(__slab_data.sd_Slabs[i].mlh_Head->mln_Succ != NULL) if(__slab_data.sd_Slabs[i].mlh_Head->mln_Succ != NULL)
D(("freeing slab #%ld (%lu bytes per chunk)", i, (1UL << i))); D(("freeing slab slot #%ld (%lu bytes per chunk)", i, (1UL << i)));
for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ; for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head, j = 0 ;
sn->sn_MinNode.mln_Succ != NULL ; sn->sn_MinNode.mln_Succ != NULL ;
sn = sn_next) sn = sn_next)
{ {
sn_next = (struct SlabNode *)sn->sn_MinNode.mln_Succ; sn_next = (struct SlabNode *)sn->sn_MinNode.mln_Succ;
D((" slab #%ld.%ld at 0x%08lx",i, ++j, sn));
D((" fragmentation = %ld%%",100 * (__slab_data.sd_StandardSlabSize - sn->sn_Count * sn->sn_ChunkSize) / __slab_data.sd_StandardSlabSize));
D((" total space used = %ld (%ld%%)",sn->sn_UseCount * sn->sn_ChunkSize, 100 * sn->sn_UseCount / sn->sn_Count));
D((" number of chunks total = %ld",sn->sn_Count));
D((" number of chunks used = %ld%s",sn->sn_UseCount,sn->sn_UseCount == 0 ? " (empty)" : (sn->sn_UseCount == sn->sn_Count) ? " (full)" : ""));
D((" how often reused = %ld",sn->sn_NumReused));
total_slab_size += sizeof(*sn) + __slab_data.sd_StandardSlabSize;
slab_count++;
FreeVec(sn); FreeVec(sn);
} }
} }
if(slab_count > 0)
D(("number of slabs = %ld, total slab size = %ld bytes",slab_count, total_slab_size));
if(__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL) if(__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL)
D(("freeing single allocations")); D(("freeing single allocations"));
/* Free the memory allocated for each allocation which did not /* Free the memory allocated for each allocation which did not
* go into a slab. * go into a slab.
*/ */
for(mn = __slab_data.sd_SingleAllocations.mlh_Head ; for(mn = __slab_data.sd_SingleAllocations.mlh_Head, j = 0 ;
mn->mln_Succ != NULL ; mn->mln_Succ != NULL ;
mn = mn_next) mn = mn_next)
{ {
mn_next = mn->mln_Succ; mn_next = mn->mln_Succ;
FreeVec(mn); ssa = (struct SlabSingleAllocation *)mn;
D((" allocation #%ld at 0x%08lx, %lu bytes", ++j, ssa, ssa->ssa_Size));
total_single_allocation_size += ssa->ssa_Size;
single_allocation_count++;
FreeMem(ssa, ssa->ssa_Size);
} }
if(single_allocation_count > 0)
D(("number of single allocations = %ld, total single allocation size = %ld", single_allocation_count, total_single_allocation_size));
__slab_data.sd_InUse = FALSE; __slab_data.sd_InUse = FALSE;
} }

View File

@ -0,0 +1,38 @@
/*
* :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.
*/
#ifndef _STDLIB_HEADERS_H
#include "stdlib_headers.h"
#endif /* _STDLIB_HEADERS_H */
/****************************************************************************/
unsigned long __slab_purge_threshold;

View File

@ -1,6 +1,6 @@
#define VERSION 1 #define VERSION 1
#define REVISION 209 #define REVISION 212
#define DATE "21.11.2016" #define DATE "27.11.2016"
#define VERS "unix.lib 1.209" #define VERS "unix.lib 1.212"
#define VSTRING "unix.lib 1.209 (21.11.2016)\r\n" #define VSTRING "unix.lib 1.212 (27.11.2016)\r\n"
#define VERSTAG "\0$VER: unix.lib 1.209 (21.11.2016)" #define VERSTAG "\0$VER: unix.lib 1.212 (27.11.2016)"

View File

@ -1 +1 @@
209 212

View File

@ -13,7 +13,7 @@ DELETE = delete all quiet
.c.o: .c.o:
@echo "Compiling $<" @echo "Compiling $<"
@$(CC) -c $(CFLAGS) $< $(CC) -c $(CFLAGS) $<
############################################################################## ##############################################################################
@ -31,7 +31,7 @@ WARNINGS = \
INCLUDE = -I../library/include INCLUDE = -I../library/include
LIB = -L../library/lib LIB = -L../library/lib
OPTIONS = -DNDEBUG -fno-builtin -fwritable-strings -DNO_INLINE_STDARG -DIEEE_FLOATING_POINT_SUPPORT OPTIONS = -DNDEBUG -fno-builtin -fwritable-strings -DNO_INLINE_STDARG -DIEEE_FLOATING_POINT_SUPPORT -DVERBOSE=1
#OPTIONS = -D__MEM_DEBUG -fno-builtin #OPTIONS = -D__MEM_DEBUG -fno-builtin
#OPTIONS = -DDEBUG -D__MEM_DEBUG -DNO_INLINE_STDARG -fno-builtin #OPTIONS = -DDEBUG -D__MEM_DEBUG -DNO_INLINE_STDARG -fno-builtin
OPTIMIZE = -O OPTIMIZE = -O
@ -49,13 +49,15 @@ LIBS = -lm -lc -lgcc
all: test fgets_test iotest sscanf_test printf_test sprintf_test \ all: test fgets_test iotest sscanf_test printf_test sprintf_test \
stack_size_test translate_test strtok_test uname simple \ stack_size_test translate_test strtok_test uname simple \
fstat_stdout_test simple_sprintf date_test sscanf_64 factorial \ fstat_stdout_test simple_sprintf date_test sscanf_64 factorial \
execvp_test setlocale rand fstat_test base_dir_nametest execvp_test setlocale rand fstat_test base_dir_nametest \
malloc-test slab-test
clean: clean:
$(DELETE) #?.o #?.map test fgets_test iotest sscanf_test printf_test \ $(DELETE) #?.o #?.map test fgets_test iotest sscanf_test printf_test \
sprintf_test stack_size_test translate_test strtok_test uname \ sprintf_test stack_size_test translate_test strtok_test uname \
simple fstat_stdout_test fstat_test simple_sprintf date_test sscanf_64 \ simple fstat_stdout_test fstat_test simple_sprintf date_test sscanf_64 \
factorial execvp_test setlocale rand base_dir_nametest factorial execvp_test setlocale rand base_dir_nametest \
malloc-test slab-test
############################################################################## ##############################################################################
@ -143,6 +145,14 @@ rand : rand.o
@echo "Linking $@" @echo "Linking $@"
$(CC) $(CFLAGS) -o $@ rand.o $(LIBS) -Wl,--cref,-M,-Map=$@.map $(CC) $(CFLAGS) -o $@ rand.o $(LIBS) -Wl,--cref,-M,-Map=$@.map
malloc-test : malloc-test.o
@echo "Linking $@"
$(CC) $(CFLAGS) -o $@ malloc-test.o $(LIBS) -Wl,--cref,-M,-Map=$@.map
slab-test : slab-test.o
@echo "Linking $@"
$(CC) $(CFLAGS) -o $@ slab-test.o $(LIBS) -Wl,--cref,-M,-Map=$@.map
############################################################################## ##############################################################################
mkid: mkid:

489
test_programs/malloc-test.c Normal file
View File

@ -0,0 +1,489 @@
/* malloc-test.c
* by Wolfram Gloger 1995, 1996
*
* This program is provided `as is', there is no warranty.
*/
#if !defined(__STDC__)
#define __STDC__ 1
#endif
#include <stdlib.h>
#include <stdio.h>
#if !defined(_WIN32)
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif
#ifndef MEMORY
#define MEMORY 4000000l
#endif
#ifndef BINS_MAX
#define BINS_MAX 32768
#endif
#define SBINS_MAX 1024
#define SIZE 4024
#define I_MAX 5000
#ifndef I_AVERAGE
#define I_AVERAGE 200
#endif
#define ACTIONS_MAX 50
#ifndef SBRK_AVG
#define SBRK_AVG 0
#endif
#ifndef MMAP_THRESH
#define MMAP_THRESH 0
#endif
#ifndef TEST
#define TEST 4 /* minimal testing */
#endif
#ifndef TEST_INC
#define TEST_INC 2047
#endif
#if defined(__i386__) || defined(__sparc__) || defined(mips) || defined(_WIN32)
#define PAGE_SIZE 4096
#elif defined(__alpha__)
#define PAGE_SIZE 8192
#elif defined(__SVR4)
#define PAGE_SIZE 8192
#else
#define PAGE_SIZE 4096 /* default */
#endif
#define RANDOM(s) (lran2(0) % (s))
/* All probabilities are parts in 1024. */
#ifndef PROB_MEMALIGN
#define PROB_MEMALIGN 0
#endif
#ifndef PROB_REALLOC
#define PROB_REALLOC 48
#endif
#ifndef PROB_CALLOC
#define PROB_CALLOC 0
#endif
struct bin {
unsigned char *ptr;
unsigned long size;
} m[BINS_MAX], sm[SBINS_MAX];
unsigned long size = SIZE, bins=0, sbins=0;
unsigned long total_size=0, total_size_max=0;
unsigned char *base_ptr;
unsigned long base_save;
long
#if __STDC__
lran2(long seed)
#else
lran2(seed) long seed;
#endif
#define LRAN2_MAX 714025l /* constants for portable */
#define IA 1366l /* random number generator */
#define IC 150889l /* (see Numerical Recipes p. 211) */
{
static int first = 1;
static long x, y, v[97];
int j;
if(seed || first) {
first = 0;
x = (IC - seed) % LRAN2_MAX;
if(x < 0) x = -x;
for(j=0; j<97; j++) {
x = (IA*x + IC) % LRAN2_MAX;
v[j] = x;
}
x = (IA*x + IC) % LRAN2_MAX;
y = x;
}
j = y % 97;
y = v[j];
x = (IA*x + IC) % LRAN2_MAX;
v[j] = x;
return y;
}
#undef IA
#undef IC
void
#if __STDC__
mem_init(unsigned char *ptr, unsigned long size)
#else
mem_init(ptr, size) unsigned char *ptr; unsigned long size;
#endif
{
unsigned long i, j;
if(size == 0) return;
if(size > sizeof(unsigned long)) {
/* Try the complete initial word. */
*(unsigned long *)ptr = (unsigned long)ptr ^ size;
i = TEST_INC;
} else
i = 0;
for(; i<size; i+=TEST_INC) {
j = (unsigned long)ptr ^ i;
ptr[i] = ((j ^ (j>>8)) & 0xFF);
}
j = (unsigned long)ptr ^ (size-1);
ptr[size-1] = ((j ^ (j>>8)) & 0xFF);
}
int
#if __STDC__
mem_check(unsigned char *ptr, unsigned long size)
#else
mem_check(ptr, size) unsigned char *ptr; unsigned long size;
#endif
{
unsigned long i, j;
if(size == 0) return 0;
if(size > sizeof(unsigned long)) {
if(*(unsigned long *)ptr != ((unsigned long)ptr ^ size)) {
printf ("failed size check: expected %x, found %x!\n",
((unsigned long) ptr ^ size), *(unsigned long *) ptr);
return 1;
}
i = TEST_INC;
} else
i = 0;
for(; i<size; i+=TEST_INC) {
j = (unsigned long)ptr ^ i;
if(ptr[i] != ((j ^ (j>>8)) & 0xFF)) return 2;
}
j = (unsigned long)ptr ^ (size-1);
if(ptr[size-1] != ((j ^ (j>>8)) & 0xFF)) {
printf ("failed last byte check: expected %x, found %x!\n",
((unsigned long) ((j ^ (j>>8)) & 0xFF)), ptr[size-1]);
return 3;
}
return 0;
}
long
#if __STDC__
random_size(long max)
#else
random_size(max) long max;
#endif
{
long r1, r2, r, max_pages;
max_pages = max/PAGE_SIZE;
if(max_pages > 0) {
r1 = RANDOM(1024);
r2 = (r1 & 7)*4;
if(r1 < 512) {
/* small value near power of two */
r = (1L << (r1 >> 6)) + r2;
} else if(r1 < 512+20) {
/* value near a multiple of the page size */
r = (RANDOM(max_pages)+1)*PAGE_SIZE + r2 - 16;
/*printf("r = %4lx\n", r);*/
} else r = RANDOM(max) + 1;
} else r = RANDOM(max) + 1;
/*if(r <= 0) exit(-1);*/
return r;
}
void
#if __STDC__
bin_alloc(struct bin *m)
#else
bin_alloc(m) struct bin *m;
#endif
{
long r, key;
unsigned long sz;
#if TEST > 0
if(mem_check(m->ptr, m->size)) {
printf("bin_alloc: memory corrupt at %p, size=%lu!\n", m->ptr, m->size);
exit(1);
}
#endif
total_size -= m->size;
r = RANDOM(1024);
if(r < PROB_MEMALIGN) {
#if !defined(_WIN32)
if(m->size > 0) free(m->ptr);
m->size = random_size(size);
#if PROB_MEMALIGN
m->ptr = (unsigned char *)memalign(4 << RANDOM(8), m->size);
#endif
#endif
} else if(r < (PROB_MEMALIGN + PROB_REALLOC)) {
if(m->size == 0) {
#ifndef __sparc__
m->ptr = NULL;
#else
/* SunOS4 does not realloc() a NULL pointer */
m->ptr = (unsigned char *)malloc(1);
#endif
}
#if TEST > 2
key = RANDOM(256);
sz = m->size;
for(r=0; r<sz; r++) m->ptr[r] = (r ^ key) & 0xFF;
#endif
m->size = random_size(size);
/*printf("realloc %d\n", (int)m->size);*/
m->ptr = (unsigned char *)realloc(m->ptr, m->size);
#if TEST > 2
if(m->size < sz) sz = m->size;
for(r=0; r<sz; r++)
if(m->ptr[r] != ((r ^ key) & 0xFF)) {
printf("realloc bug !\n");
exit(1);
}
#endif
} else if(r < (PROB_MEMALIGN + PROB_REALLOC + PROB_CALLOC)) {
if(m->size > 0) free(m->ptr);
m->size = random_size(size);
m->ptr = (unsigned char *)calloc(m->size, 1);
#if TEST > 2
for(r=0; r<m->size; r++)
if(m->ptr[r] != '\0') {
printf("calloc bug !\n");
exit(1);
}
#endif
} else { /* normal malloc call */
if(m->size > 0) free(m->ptr);
m->size = random_size(size);
m->ptr = (unsigned char *)malloc(m->size);
}
if(!m->ptr) {
printf("out of memory!\n");
exit(1);
}
total_size += m->size;
if(total_size > total_size_max) total_size_max = total_size;
#if TEST > 0
mem_init(m->ptr, m->size);
#endif
if(m->ptr < base_ptr) {
#ifdef VERBOSE
printf("hmmm, allocating below brk...\n");
#endif
base_ptr = m->ptr;
}
}
void
#if __STDC__
bin_free(struct bin *m)
#else
bin_free(m) struct bin *m;
#endif
{
if(m->size == 0) return;
#if TEST > 0
if(mem_check(m->ptr, m->size)) {
printf("bin_free: memory corrupt!\n");
exit(1);
}
#endif
total_size -= m->size;
free(m->ptr);
m->size = 0;
}
void
bin_test()
{
unsigned int b;
int v;
// printf ("bin_test.\n");
for(b=0; b<bins; b++) {
if(v = mem_check(m[b].ptr, m[b].size)) {
printf("bin_test: memory corrupt! m[%d].ptr = %x, m[%d].size = %d\n",
b, m[b].ptr, b, m[b].size);
printf ("error = %d\n", v);
exit(1);
}
}
for(b=0; b<sbins; b++) {
if(mem_check(sm[b].ptr, sm[b].size)) {
printf("bin_test: memory corrupt! sm[%d].ptr = %x, sm[%d].size = %d\n",
b, sm[b].ptr, b, sm[b].size);
exit(1);
}
}
}
void
print_times()
{
#if !defined(_WIN32) && !defined(AMIGA)
struct rusage ru;
long total_sec, total_usec;
getrusage(RUSAGE_SELF, &ru);
printf(" u=%ld.%06ldsec",
(long)ru.ru_utime.tv_sec, (long)ru.ru_utime.tv_usec);
printf(" s=%ld.%06ldsec",
(long)ru.ru_stime.tv_sec, (long)ru.ru_stime.tv_usec);
total_usec = (long)ru.ru_utime.tv_usec + (long)ru.ru_stime.tv_usec;
total_sec = (long)ru.ru_utime.tv_sec + (long)ru.ru_stime.tv_sec;
if(total_usec >= 1000000) {
total_usec -= 1000000;
total_sec++;
}
printf(" t=%ld.%06ldsec", total_sec, total_usec);
#endif
}
int
#if __STDC__
main(int argc, char *argv[])
#else
main(argc, argv) int argc; char *argv[];
#endif
{
int i, j, next_i, count, max=I_MAX, actions;
unsigned int b;
long sbrk_max, sum;
double sbrk_used_sum, total_size_sum;
void* dummy = 0;
if(argc > 1) max = atoi(argv[1]);
if(argc > 2) size = atoi(argv[2]);
lran2((long)max ^ size);
bins = (MEMORY/size)*4;
if(bins > BINS_MAX) bins = BINS_MAX;
#if 0 // FIX ME? Disable sbrk...
base_ptr = (unsigned char *)sbrk(0);
sum = (long)base_ptr % PAGE_SIZE;
if(sum > 0) {
if((char *)sbrk((long)PAGE_SIZE - sum) == (char *)-1) exit(1);
base_ptr += (long)PAGE_SIZE - sum;
/*printf("base_ptr = %lx\n", (long)base_ptr);*/
}
/* attempt to fill up the region below the initial brk */
for(i=0; i<10000; i++) {
dummy = malloc(1);
if(dummy >= (void*)base_ptr) break;
}
free(dummy);
base_save = ((unsigned long)base_ptr >> 24) << 24;
#endif
#if MMAP_THRESH > 0
if(!mallopt(-3, MMAP_THRESH)) printf("mallopt failed!\n");
if(!mallopt(-4, 200)) printf("mallopt failed!\n");
#endif
#ifdef VERBOSE
printf("# mmap_thresh=%d\n", MMAP_THRESH);
printf("# bins=%d max=%d size=%d\n", bins, max, size);
printf("# base=%lx\n", base_save);
#endif
for(b=0; b<bins; b++) {
if(RANDOM(2) == 0) bin_alloc(&m[b]);
else m[b].size = 0;
}
sbrk_max = 0;
sbrk_used_sum = total_size_sum = 0.0;
for(i=next_i=count=0; i<=max;) {
#if TEST > 1
bin_test();
#endif
#ifdef MSTATS
malloc_stats();
#endif
actions = RANDOM(ACTIONS_MAX);
for(j=0; j<actions; j++) {
b = RANDOM(bins);
bin_free(&m[b]);
#if TEST > 3
bin_test();
#endif
}
i += actions;
#ifdef AFTER_FREE
AFTER_FREE;
#endif
#if SBRK_AVG > 0
if(sbins<SBINS_MAX && RANDOM(SBRK_AVG)==0) {
/* throw in an explicit sbrk call */
sm[sbins].size = RANDOM(10000)+1;
sm[sbins].ptr = sbrk(sm[sbins].size);
if(sbins>0 && sm[sbins].ptr==(sm[sbins-1].ptr+sm[sbins-1].size)) {
sm[sbins-1].size += sm[sbins].size;
sbins--;
}
#ifdef VERBOSE
printf("sbrk #%d %p %ld\n", sbins, sm[sbins].ptr, sm[sbins].size);
#endif
#if TEST > 0
mem_init(sm[sbins].ptr, sm[sbins].size);
#endif
sbins++;
}
#endif
actions = RANDOM(ACTIONS_MAX);
for(j=0; j<actions; j++) {
b = RANDOM(bins);
bin_alloc(&m[b]);
#if TEST > 3
bin_test();
#endif
}
i += actions;
if(i >= next_i) { /* gather statistics */
count++;
#if !defined(_WIN32) && !defined(AMIGA)
sum = (long)sbrk(0);
#else
sum = 0;
#endif
if(sum > sbrk_max) sbrk_max = sum;
sbrk_used_sum += sum;
total_size_sum += (double)total_size;
#ifdef VERBOSE
printf("%8d %7lu\n", i, total_size);
#endif
next_i += I_AVERAGE;
}
}
/* Correct sbrk values. */
sbrk_max -= (long)base_ptr;
sbrk_used_sum -= (double)count*(long)base_ptr;
#ifdef VERBOSE
printf("# initial brk: %lx\n", (long)base_ptr);
printf("# max. sbrk()'ed memory: %ld bytes\n", sbrk_max);
printf("# avg. sbrk()'ed memory: %ld bytes\n",
(long)(sbrk_used_sum/count));
printf("# current size allocated: %ld bytes\n", total_size);
printf("# maximum size allocated: %ld bytes\n", total_size_max);
printf("# average size allocated: %.1f bytes\n", total_size_sum/count);
printf("# current heap waste: %.2f%%\n",
(1.0 - (double)total_size_max/sbrk_max)*100.0);
printf("# average heap waste: %.2f%%\n",
(1.0 - (double)total_size_sum/sbrk_used_sum)*100.0);
printf("# total sbrk calls performed: %d\n", sbins);
#else
printf("size=%7ld waste=%7.3f%%", size,
/* (1.0 - (double)total_size_max/sbrk_max)*100.0, */
(1.0 - (double)total_size_sum/sbrk_used_sum)*100.0);
print_times();
printf("\n");
#endif
return 0;
}
/*
* Local variables:
* tab-width:4
* compile-command: "gcc -Wall malloc-test.c -o malloc-test"
* End:
*/

26
test_programs/slab-test.c Normal file
View File

@ -0,0 +1,26 @@
#include <stdlib.h>
#include <stdio.h>
unsigned long __slab_max_size = 4096;
static int print_json(void * ignore,const char * buffer,size_t len)
{
fputs(buffer, stdout);
return(0);
}
int
main(int argc,char ** argv)
{
int i;
srand(1);
for(i = 0 ; i < 1000 ; i++)
malloc(1 + (rand() % 8192));
__get_slab_stats(NULL, print_json);
return(0);
}