diff --git a/include/freetype/config/ftstdlib.h b/include/freetype/config/ftstdlib.h index 30ec14e..db6b265 100644 --- a/include/freetype/config/ftstdlib.h +++ b/include/freetype/config/ftstdlib.h @@ -81,6 +81,7 @@ #define ft_memmove memmove #define ft_memset memset #define ft_strcat strcat +#define ft_strchr strchr #define ft_strcmp strcmp #define ft_strcpy strcpy #define ft_strlen strlen diff --git a/include/freetype/internal/fttrace.h b/include/freetype/internal/fttrace.h index e9b383a..5d2decc 100644 --- a/include/freetype/internal/fttrace.h +++ b/include/freetype/internal/fttrace.h @@ -24,6 +24,7 @@ FT_TRACE_DEF( any ) /* base components */ FT_TRACE_DEF( calc ) /* calculations (ftcalc.c) */ FT_TRACE_DEF( memory ) /* memory manager (ftobjs.c) */ +FT_TRACE_DEF( dbgmem ) /* memory debugger (ftdbgmem.c) */ FT_TRACE_DEF( stream ) /* stream manager (ftstream.c) */ FT_TRACE_DEF( io ) /* i/o interface (ftsystem.c) */ FT_TRACE_DEF( list ) /* list management (ftlist.c) */ diff --git a/src/base/ftdbgmem.c b/src/base/ftdbgmem.c index 12fed04..bb04f68 100644 --- a/src/base/ftdbgmem.c +++ b/src/base/ftdbgmem.c @@ -25,6 +25,10 @@ #include FT_TYPES_H +#undef FT_COMPONENT +#define FT_COMPONENT trace_dbgmem + + #ifdef FT_DEBUG_MEMORY #define KEEPALIVE /* `Keep alive' means that freed blocks aren't released @@ -505,6 +509,65 @@ } + /* + * ft_mem_get_val_for_source( source, env_var_name ) + * + * Get a numerical value from named environmental variable + * for the site specified by FT_MemSource. For the syntax + * of the environmental variable, see docs/DEBUG. + * + */ + static int + ft_mem_get_env_val_for_source( FT_MemSource source, + const char* env_var_name ) + { + char *file_name = (char*)source->file_name; + char *c, *c0; + + + /* return if specified environment is unset */ + c = c0 = getenv( env_var_name ); + if ( !c ) + return -1; + + /* return if basename not found anymore */ + while ( NULL != ( c = ft_strstr( c, file_name ) ) ) + { + /* matched token is 1st or after separator space? */ + if ( c != c0 && *(c-1) != ' ' ) + goto NextToken; + + /* check ':', a separator between pathname & line number */ + c = c + ft_strlen( file_name ); + if ( ':' != *c || !ft_isdigit( c[1] ) ) + goto NextToken; + c++; + + /* check the line number */ + if ( atoi( c ) != source->line_no ) + goto NextToken; + + /* line number matched, skip digits */ + while ( ft_isdigit( *c ) ) + c++; + + /* check ',', a separator between line number & value */ + if ( ',' != *c || !ft_isdigit( c[1] ) ) + goto NextToken; + c++; + return atoi( c ); + + NextToken: + c = ft_strchr( c, ' ' ); + if ( !c ) /* no token anymore */ + break; + + c++; + } + return -1; + } + + static void ft_mem_table_set( FT_MemTable table, FT_Byte* address, @@ -676,17 +739,87 @@ } + static FT_Bool + ft_mem_check_alloc_limiter_for_source( FT_MemSource source, + FT_Long size ) + { + int mem_limit_site_total = -1; /* negative means unlimited */ + int mem_limit_site_cur = -1; /* negative means unlimited */ + FT_Bool exceeds_limit_site_total = FALSE; + FT_Bool exceeds_limit_site_cur = FALSE; + long req_all = source->all_size + size; + long req_cur = source->cur_size + size; +#define FT_HAS_LIMITER( key ) \ + ( mem_limit_ ## key ## _total >= 0 || mem_limit_ ## key ## _cur >= 0 ) +#define FT_HAS_MULTI_LIMITERS( key ) \ + ( mem_limit_ ## key ## _total >= 0 && mem_limit_ ## key ## _cur >= 0 ) +#define FT_TRACE6_DEFINED_LIMIT( req, limit ) \ + { \ + if ( limit >= 0 ) \ + FT_TRACE6(( "%ld =< %ld", req, limit )); \ + } + + mem_limit_site_total = + ft_mem_get_env_val_for_source( source, + "FT2_ALLOC_TOTAL_MAX_SITE" ); + mem_limit_site_cur = + ft_mem_get_env_val_for_source( source, + "FT2_ALLOC_CUR_MAX_SITE" ); + if ( FT_HAS_LIMITER( site ) ) + FT_TRACE6(( "ft_mem_table_set() invoked by %s:%lu, limit:( ", + source->file_name, source->line_no )); + FT_TRACE6_DEFINED_LIMIT( req_all, mem_limit_site_total ); + if ( FT_HAS_MULTI_LIMITERS( site ) ) + FT_TRACE6(( " && " )); + FT_TRACE6_DEFINED_LIMIT( req_cur, mem_limit_site_cur ); + if ( FT_HAS_LIMITER( site ) ) + FT_TRACE6((" )\n")); + + if ( mem_limit_site_total >= 0 && mem_limit_site_total > req_all ) + exceeds_limit_site_total = TRUE; + + if ( mem_limit_site_cur >= 0 && mem_limit_site_cur > req_cur ) + exceeds_limit_site_cur = TRUE; + + if ( exceeds_limit_site_total || exceeds_limit_site_cur ) + { + FT_TRACE6(( "ft_mem_table_set() returns NULL to %s:%lu," + " allocation request exceeds %s-limit (%lu > %lu)\n", + source->file_name, source->line_no, + ( exceeds_limit_site_total ? "site-total" : + "site-current" ), + ( exceeds_limit_site_total ? req_all : + req_cur ), + ( exceeds_limit_site_total ? mem_limit_site_total : + mem_limit_site_cur ) + )); + return FALSE; + } + return TRUE; + +#undef FT_HAS_LIMITER +#undef FT_HAS_MULTI_LIMITERS +#undef FT_TRACE6_DEFINED_LIMIT + } + + extern FT_Pointer ft_mem_debug_alloc( FT_Memory memory, FT_Long size ) { - FT_MemTable table = (FT_MemTable)memory->user; - FT_Byte* block; + FT_MemTable table = (FT_MemTable)memory->user; + FT_MemSource source = NULL; + FT_Byte* block; if ( size <= 0 ) ft_mem_debug_panic( "negative block size allocation (%ld)", size ); + source = ft_mem_table_get_source( table ); + if ( source ) + if ( !ft_mem_check_alloc_limiter_for_source( source, size ) ) + return NULL; + /* return NULL if the maximum number of allocations was reached */ if ( table->bound_count && table->alloc_count >= table->alloc_count_max ) diff --git a/src/base/ftrfork.c b/src/base/ftrfork.c index 6df2def..b01ecb9 100644 --- a/src/base/ftrfork.c +++ b/src/base/ftrfork.c @@ -25,6 +25,7 @@ #include +#include FT_CONFIG_CONFIG_H #include FT_INTERNAL_DEBUG_H #include FT_INTERNAL_STREAM_H #include FT_INTERNAL_RFORK_H diff --git a/src/cache/ftccache.c b/src/cache/ftccache.c index a5a915e..18af754 100644 --- a/src/cache/ftccache.c +++ b/src/cache/ftccache.c @@ -473,6 +473,29 @@ } + FT_LOCAL_DEF( FT_Size ) + ftc_get_list_length( FTC_Cache cache, + FT_PtrDist hash ) + { + FT_Size len = 0; + FTC_Node* bucket; + FTC_Node* pnode; + FT_UFast idx; + + + idx = hash & cache->mask; + if ( idx < cache->p ) + idx = hash & ( cache->mask * 2 + 1 ); + + bucket = cache->buckets + idx; + pnode = bucket; + for ( ; pnode && *pnode; pnode = &((*pnode)->link) ) + len++; + + return len; + } + + #ifndef FTC_INLINE FT_LOCAL_DEF( FT_Error ) diff --git a/src/cache/ftccache.h b/src/cache/ftccache.h index 10830a9..176f766 100644 --- a/src/cache/ftccache.h +++ b/src/cache/ftccache.h @@ -196,6 +196,10 @@ FT_BEGIN_HEADER FTC_Cache_RemoveFaceID( FTC_Cache cache, FTC_FaceID face_id ); + FT_LOCAL( FT_Size ) + ftc_get_list_length( FTC_Cache cache, + FT_PtrDist hash ); + #ifdef FTC_INLINE @@ -208,6 +212,12 @@ FT_BEGIN_HEADER FT_UFast _idx; \ \ \ + FT_TRACE6(("FTC_CACHE_LOOKUP_CMP() lookup object for ")); \ + FT_TRACE6(("hash=0x%08x in cache ", _hash )); \ + FT_TRACE6(("mask=0x%08x p=0x%08x ", _cache->mask, _cache->p )); \ + FT_TRACE7(("list length=%d", ftc_get_list_length( _cache, _hash ) )); \ + FT_TRACE6(("\n")); \ + \ error = FTC_Err_Ok; \ node = NULL; \ _idx = _hash & _cache->mask; \ @@ -227,6 +237,8 @@ FT_BEGIN_HEADER _pnode = &_node->link; \ } \ \ + FT_TRACE6(("(_bucket,_pnode)=(%p,%p)\n", _bucket, _pnode)); \ + \ if ( _node != *_bucket ) \ { \ *_pnode = _node->link; \ diff --git a/src/cache/ftcmanag.c b/src/cache/ftcmanag.c index 548ebe9..fcf1c43 100644 --- a/src/cache/ftcmanag.c +++ b/src/cache/ftcmanag.c @@ -622,6 +622,24 @@ } + static int + ft_get_length_nodes_list( FTC_Node node0 ) + { + FTC_Node node = node0; + int len = 0; + + + while ( node ) + { + len ++; + node = node->link; + if ( node == node0 ) + return len; + } + return len; + } + + FT_LOCAL_DEF( FT_UInt ) FTC_Manager_FlushN( FTC_Manager manager, FT_UInt count ) @@ -631,9 +649,17 @@ FT_UInt result; + FT_TRACE2(( "FTC_Manager_FlushN() tries to" + " free %d nodes from list length=%d\n", + count, + ft_get_length_nodes_list( manager->nodes_list ) )); + /* try to remove `count' nodes from the list */ if ( first == NULL ) /* empty list! */ + { + FT_TRACE2(("FTC_Manager_FlushN() cannot change empty list\n" )); return 0; + } /* go to last node - it's a circular list */ node = FTC_NODE__PREV(first); @@ -654,6 +680,10 @@ node = prev; } + FT_TRACE2(( "FTC_Manager_FlushN() freed %d nodes," + " list length=%d\n", + result, + ft_get_length_nodes_list( manager->nodes_list ) )); return result; } diff --git a/src/cache/ftcsbits.c b/src/cache/ftcsbits.c index 0386bdd..907868c 100644 --- a/src/cache/ftcsbits.c +++ b/src/cache/ftcsbits.c @@ -125,6 +125,11 @@ sbit->buffer = 0; error = clazz->family_load_glyph( family, gindex, manager, &face ); + FT_TRACE2(( "ftc_snode_load() got %s from " + "family_load_glyph() err=0x%02x%s\n", + error ? "error" : "ok", + error, + error ? " fallback to BadGlyph" : "" )); if ( error ) goto BadGlyph; @@ -200,6 +205,8 @@ *asize = 0; } + FT_TRACE2(( "ftc_snode_load() load glyph for gid=%d, err=0x%02x\n", + gindex, error)); return error; } @@ -333,6 +340,7 @@ FT_Bool result; + FT_TRACE2(( "ftc_snode_compare() work for gindex=%d\n", gindex )); result = FT_BOOL( gnode->family == gquery->family && (FT_UInt)( gindex - gnode->gindex ) < snode->count ); if ( result ) @@ -397,6 +405,8 @@ } } + FT_TRACE2(( "ftc_snode_compare() returns %s\n", + result ? "TRUE" : "FALSE" )); return result; }