From 79b252dfdf15c1706d33d880f613b7b25af8195b Mon Sep 17 00:00:00 2001 From: Robert Walsh Date: Sat, 19 Jun 2004 18:12:36 +0000 Subject: [PATCH] Memory pool support. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2428 --- addrcheck/ac_main.c | 6 +- coregrind/docs/coregrind_core.html | 17 ++++ include/valgrind.h | 37 +++++++ memcheck/mac_malloc_wrappers.c | 155 +++++++++++++++++++++++++---- memcheck/mac_needs.c | 74 +++++++++++++- memcheck/mac_shared.h | 36 ++++++- memcheck/mc_clientreqs.c | 37 ++++++- memcheck/tests/Makefile.am | 6 +- memcheck/tests/mempool.c | 150 ++++++++++++++++++++++++++++ memcheck/tests/mempool.stderr.exp | 38 +++++++ memcheck/tests/mempool.vgtest | 2 + 11 files changed, 528 insertions(+), 30 deletions(-) create mode 100644 memcheck/tests/mempool.c create mode 100644 memcheck/tests/mempool.stderr.exp create mode 100644 memcheck/tests/mempool.vgtest diff --git a/addrcheck/ac_main.c b/addrcheck/ac_main.c index aac81534ed..67f1ce8804 100644 --- a/addrcheck/ac_main.c +++ b/addrcheck/ac_main.c @@ -1185,7 +1185,11 @@ Bool SK_(handle_client_request) ( ThreadId tid, UInt* arg_block, UInt *ret ) /* Overload memcheck client reqs */ if (!VG_IS_SKIN_USERREQ('M','C',arg[0]) && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0] - && VG_USERREQ__FREELIKE_BLOCK != arg[0]) + && VG_USERREQ__FREELIKE_BLOCK != arg[0] + && VG_USERREQ__CREATE_MEMPOOL != arg[0] + && VG_USERREQ__DESTROY_MEMPOOL != arg[0] + && VG_USERREQ__MEMPOOL_ALLOC != arg[0] + && VG_USERREQ__MEMPOOL_FREE != arg[0]) return False; switch (arg[0]) { diff --git a/coregrind/docs/coregrind_core.html b/coregrind/docs/coregrind_core.html index 0a01661b03..c6d6722b46 100644 --- a/coregrind/docs/coregrind_core.html +++ b/coregrind/docs/coregrind_core.html @@ -984,6 +984,23 @@ tool-specific documentation for explanations of the tool-specific macros). with VALGRIND_MALLOCLIKE_BLOCK. Again, see memcheck/memcheck.h for information on how to use it.

+

  • VALGRIND_CREATE_MEMPOOL: This is similar to + VALGRIND_MALLOCLIKE_BLOCK, but is tailored towards code + that uses memory pools. See the comments in valgrind.h + for information on how to use it. +

    +

  • VALGRIND_DESTROY_MEMPOOL: This should be used in + conjunction with VALGRIND_CREATE_MEMPOOL Again, see the + comments in valgrind.h for information on how to use it. +

    +

  • VALGRIND_MEMPOOL_ALLOC: This should be used in + conjunction with VALGRIND_CREATE_MEMPOOL Again, see the + comments in valgrind.h for information on how to use it. +

    +

  • VALGRIND_MEMPOOL_FREE: This should be used in + conjunction with VALGRIND_CREATE_MEMPOOL Again, see the + comments in valgrind.h for information on how to use it. +

  • VALGRIND_NON_SIMD_CALL[0123]: executes a function of 0, 1, 2 or 3 args in the client program on the real CPU, not the virtual CPU that Valgrind normally runs code on. These are used in various ways diff --git a/include/valgrind.h b/include/valgrind.h index 8cd07c9eba..33d7be380c 100644 --- a/include/valgrind.h +++ b/include/valgrind.h @@ -163,6 +163,11 @@ typedef malloc() et al, by using vg_replace_malloc.c. */ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, /* Allow printfs to valgrind log. */ VG_USERREQ__PRINTF = 0x1401, @@ -322,4 +327,36 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...) addr, rzB, 0, 0); \ } +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0); \ + } + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0); \ + } + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0); \ + } + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0); \ + } + #endif /* __VALGRIND_H */ diff --git a/memcheck/mac_malloc_wrappers.c b/memcheck/mac_malloc_wrappers.c index b3c5353287..45c0ec1c05 100644 --- a/memcheck/mac_malloc_wrappers.c +++ b/memcheck/mac_malloc_wrappers.c @@ -61,6 +61,10 @@ Bool (*MAC_(check_noaccess))( Addr a, UInt len, Addr* bad_addr ) = NULL; /* Record malloc'd blocks. Nb: Addrcheck and Memcheck construct this separately in their respective initialisation functions. */ VgHashTable MAC_(malloc_list) = NULL; + +/* Memory pools. Nb: Addrcheck and Memcheck construct this separately + in their respective initialisation functions. */ +VgHashTable MAC_(mempool_list) = NULL; /* Records blocks after freeing. */ static MAC_Chunk* freed_list_start = NULL; @@ -127,7 +131,8 @@ MAC_Chunk* MAC_(first_matching_freed_MAC_Chunk) ( Bool (*p)(MAC_Chunk*) ) /* Allocate its shadow chunk, put it on the appropriate list. */ static -void add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind ) +MAC_Chunk* add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind, + VgHashTable table) { MAC_Chunk* mc; @@ -146,7 +151,9 @@ void add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind ) VG_(skin_panic)("add_MAC_chunk: shadow area is accessible"); } - VG_(HT_add_node)( MAC_(malloc_list), (VgHashNode*)mc ); + VG_(HT_add_node)( table, (VgHashNode*)mc ); + + return mc; } /*------------------------------------------------------------*/ @@ -155,21 +162,26 @@ void add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind ) /* Allocate memory and note change in memory available */ __inline__ -void MAC_(new_block) ( Addr p, UInt size, - UInt rzB, Bool is_zeroed, MAC_AllocKind kind ) +MAC_Chunk* MAC_(new_block) ( Addr p, UInt size, + UInt rzB, Bool is_zeroed, MAC_AllocKind kind, + VgHashTable table) { + MAC_Chunk *mc; + VGP_PUSHCC(VgpCliMalloc); cmalloc_n_mallocs ++; cmalloc_bs_mallocd += size; - add_MAC_Chunk( p, size, kind ); + mc = add_MAC_Chunk( p, size, kind, table ); MAC_(ban_mem_heap)( p-rzB, rzB ); MAC_(new_mem_heap)( p, size, is_zeroed ); MAC_(ban_mem_heap)( p+size, rzB ); VGP_POPCC(VgpCliMalloc); + + return mc; } void* SK_(malloc) ( Int n ) @@ -180,7 +192,8 @@ void* SK_(malloc) ( Int n ) } else { Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n ); MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB), - /*is_zeroed*/False, MAC_AllocMalloc ); + /*is_zeroed*/False, MAC_AllocMalloc, + MAC_(malloc_list)); return (void*)p; } } @@ -193,7 +206,8 @@ void* SK_(__builtin_new) ( Int n ) } else { Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n ); MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB), - /*is_zeroed*/False, MAC_AllocNew ); + /*is_zeroed*/False, MAC_AllocNew, + MAC_(malloc_list)); return (void*)p; } } @@ -207,7 +221,8 @@ void* SK_(__builtin_vec_new) ( Int n ) } else { Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n ); MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB), - /*is_zeroed*/False, MAC_AllocNewVec ); + /*is_zeroed*/False, MAC_AllocNewVec, + MAC_(malloc_list)); return (void*)p; } } @@ -220,7 +235,8 @@ void* SK_(memalign) ( Int align, Int n ) } else { Addr p = (Addr)VG_(cli_malloc)( align, n ); MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB), - /*is_zeroed*/False, MAC_AllocMalloc ); + /*is_zeroed*/False, MAC_AllocMalloc, + MAC_(malloc_list)); return (void*)p; } } @@ -238,7 +254,8 @@ void* SK_(calloc) ( Int nmemb, Int size1 ) } else { Addr p = (Addr)VG_(cli_malloc)( VG_(clo_alignment), n ); MAC_(new_block) ( p, n, VG_(vg_malloc_redzone_szB), - /*is_zeroed*/True, MAC_AllocMalloc ); + /*is_zeroed*/True, MAC_AllocMalloc, + MAC_(malloc_list)); for (i = 0; i < n; i++) ((UChar*)p)[i] = 0; return (void*)p; @@ -261,17 +278,15 @@ void die_and_free_mem ( MAC_Chunk* mc, describe_addr() which looks for it in malloclist. */ *prev_chunks_next_ptr = mc->next; - /* Record where freed */ - mc->where = VG_(get_ExeContext) ( VG_(get_current_or_recent_tid)() ); - /* Put it out of harm's way for a while, if not from a client request */ - if (MAC_AllocCustom != mc->allockind) + if (MAC_AllocCustom != mc->allockind) { + /* Record where freed */ + mc->where = VG_(get_ExeContext) ( VG_(get_current_or_recent_tid)() ); add_to_freed_queue ( mc ); - else + } else VG_(free) ( mc ); } - __inline__ void MAC_(handle_free) ( Addr p, UInt rzB, MAC_AllocKind kind ) { @@ -392,13 +407,119 @@ void* SK_(realloc) ( void* p, Int new_size ) /* this has to be after die_and_free_mem, otherwise the former succeeds in shorting out the new block, not the old, in the case when both are on the same list. */ - add_MAC_Chunk ( p_new, new_size, MAC_AllocMalloc ); + add_MAC_Chunk ( p_new, new_size, MAC_AllocMalloc, MAC_(malloc_list) ); VGP_POPCC(VgpCliMalloc); return (void*)p_new; } } +/* Memory pool stuff. */ + +void MAC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed) +{ + MAC_Mempool* mp; + + mp = VG_(malloc)(sizeof(MAC_Mempool)); + mp->pool = pool; + mp->rzB = rzB; + mp->is_zeroed = is_zeroed; + mp->chunks = VG_(HT_construct)(); + + /* Paranoia ... ensure this area is off-limits to the client, so + the mp->data field isn't visible to the leak checker. If memory + management is working correctly, anything pointer returned by + VG_(malloc) should be noaccess as far as the client is + concerned. */ + if (!MAC_(check_noaccess)( (Addr)mp, sizeof(MAC_Mempool), NULL )) { + VG_(skin_panic)("MAC_(create_mempool): shadow area is accessible"); + } + + VG_(HT_add_node)( MAC_(mempool_list), (VgHashNode*)mp ); + +} + +void MAC_(destroy_mempool)(Addr pool) +{ + MAC_Mempool* mp; + MAC_Mempool** prev_next; + + void nuke_chunk(VgHashNode *node) + { + MAC_Chunk *mc = (MAC_Chunk *)node; + + /* Note: ban redzones again -- just in case user de-banned them + with a client request... */ + MAC_(ban_mem_heap)(mc->data-mp->rzB, mp->rzB ); + MAC_(die_mem_heap)(mc->data, mc->size ); + MAC_(ban_mem_heap)(mc->data+mc->size, mp->rzB ); + } + + mp = (MAC_Mempool*)VG_(HT_get_node) ( MAC_(mempool_list), (UInt)pool, + (VgHashNode***)&prev_next ); + + if (mp == NULL) { + ThreadId tid = VG_(get_current_or_recent_tid)(); + + MAC_(record_illegal_mempool_error) ( tid, pool ); + return; + } + + *prev_next = mp->next; + VG_(HT_apply_to_all_nodes)(mp->chunks, nuke_chunk); + VG_(HT_destruct)(mp->chunks); + + VG_(free)(mp); +} + +void MAC_(mempool_alloc)(Addr pool, Addr addr, UInt size) +{ + MAC_Mempool* mp; + MAC_Mempool** prev_next; + MAC_Chunk* mc; + + mp = (MAC_Mempool*)VG_(HT_get_node) ( MAC_(mempool_list), (UInt)pool, + (VgHashNode***)&prev_next ); + + if (mp == NULL) { + ThreadId tid = VG_(get_current_or_recent_tid)(); + + MAC_(record_illegal_mempool_error) ( tid, pool ); + return; + } + + mc = MAC_(new_block)(addr, size, mp->rzB, mp->is_zeroed, MAC_AllocCustom, + mp->chunks); +} + +void MAC_(mempool_free)(Addr pool, Addr addr) +{ + MAC_Mempool* mp; + MAC_Mempool** prev_pool; + MAC_Chunk* mc; + MAC_Chunk** prev_chunk; + ThreadId tid = VG_(get_current_or_recent_tid)(); + + + mp = (MAC_Mempool*)VG_(HT_get_node)(MAC_(mempool_list), (UInt)pool, + (VgHashNode***)&prev_pool); + + if (mp == NULL) { + MAC_(record_illegal_mempool_error)(tid, pool); + return; + } + + mc = (MAC_Chunk*)VG_(HT_get_node)(mp->chunks, (UInt)addr, + (VgHashNode***)&prev_chunk); + + if (mc == NULL) { + MAC_(record_free_error)(tid, (Addr)addr); + return; + } + + die_and_free_mem(mc, prev_chunk, mp->rzB); +} + void MAC_(print_malloc_stats) ( void ) { UInt nblocks = 0, nbytes = 0; diff --git a/memcheck/mac_needs.c b/memcheck/mac_needs.c index 29772a2485..8e1a7b8a4e 100644 --- a/memcheck/mac_needs.c +++ b/memcheck/mac_needs.c @@ -196,6 +196,9 @@ Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 ) VG_(skin_panic)("Shouldn't get LeakErr in SK_(eq_SkinError),\n" "since it's handled with VG_(unique_error)()!"); + case IllegalMempoolErr: + return True; + default: VG_(printf)("Error:\n unknown error code %d\n", VG_(get_error_kind)(e1)); @@ -223,9 +226,15 @@ void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) " Address 0x%x is not stack'd, malloc'd or (recently) free'd",a); } break; - case Freed: case Mallocd: case UserG: { + case Freed: case Mallocd: case UserG: case Mempool: { UInt delta; UChar* relative; + UChar* kind; + if (ai->akind == Mempool) { + kind = "mempool"; + } else { + kind = "block"; + } if (ai->rwoffset < 0) { delta = (UInt)(- ai->rwoffset); relative = "before"; @@ -237,8 +246,8 @@ void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) relative = "inside"; } VG_(message)(Vg_UserMsg, - " Address 0x%x is %d bytes %s a block of size %d %s", - a, delta, relative, + " Address 0x%x is %d bytes %s a %s of size %d %s", + a, delta, relative, kind, ai->blksize, ai->akind==Mallocd ? "alloc'd" : ai->akind==Freed ? "free'd" @@ -314,6 +323,12 @@ void MAC_(pp_shared_SkinError) ( Error* err ) break; } + case IllegalMempoolErr: + VG_(message)(Vg_UserMsg, "Illegal memory pool address"); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + default: VG_(printf)("Error:\n unknown Memcheck/Addrcheck error code %d\n", VG_(get_error_kind)(err)); @@ -470,6 +485,16 @@ void MAC_(record_free_error) ( ThreadId tid, Addr a ) VG_(maybe_record_error)( tid, FreeErr, a, /*s*/NULL, &err_extra ); } +void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.addrinfo.akind = Undescribed; + VG_(maybe_record_error)( tid, IllegalMempoolErr, a, /*s*/NULL, &err_extra ); +} + void MAC_(record_freemismatch_error) ( ThreadId tid, Addr a ) { MAC_Error err_extra; @@ -499,6 +524,7 @@ UInt SK_(update_extra)( Error* err ) case ParamErr: case UserErr: case FreeErr: + case IllegalMempoolErr: case FreeMismatchErr: { MAC_Error* extra = (MAC_Error*)VG_(get_error_extra)(err); if (extra != NULL && Undescribed == extra->addrinfo.akind) { @@ -533,6 +559,7 @@ Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su ) else if (VG_STREQ(name, "Free")) skind = FreeSupp; else if (VG_STREQ(name, "Leak")) skind = LeakSupp; else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp; + else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp; else return False; @@ -595,6 +622,9 @@ Bool SK_(error_matches_suppression)(Error* err, Supp* su) case LeakSupp: return (ekind == LeakErr); + case MempoolSupp: + return (ekind == IllegalMempoolErr); + default: VG_(printf)("Error:\n" " unknown suppression type %d\n", @@ -611,6 +641,7 @@ Char* SK_(get_error_name) ( Error* err ) case ParamErr: return "Param"; case UserErr: return NULL; /* Can't suppress User errors */ case FreeMismatchErr: return "Free"; + case IllegalMempoolErr: return "Mempool"; case FreeErr: return "Free"; case AddrErr: switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) { @@ -784,6 +815,7 @@ static void done_prof_mem ( void ) { } void MAC_(common_pre_clo_init)(void) { MAC_(malloc_list) = VG_(HT_construct)(); + MAC_(mempool_list) = VG_(HT_construct)(); init_prof_mem(); } @@ -844,7 +876,8 @@ Bool MAC_(handle_common_client_requests)(ThreadId tid, UInt* arg, UInt* ret ) UInt rzB = arg[3]; Bool is_zeroed = (Bool)arg[4]; - MAC_(new_block) ( p, sizeB, rzB, is_zeroed, MAC_AllocCustom ); + MAC_(new_block) ( p, sizeB, rzB, is_zeroed, MAC_AllocCustom, + MAC_(malloc_list) ); return True; } case VG_USERREQ__FREELIKE_BLOCK: { @@ -859,6 +892,39 @@ Bool MAC_(handle_common_client_requests)(ThreadId tid, UInt* arg, UInt* ret ) *ret = (Addr)MAC_(record_overlap_error); return True; + case VG_USERREQ__CREATE_MEMPOOL: { + Addr pool = (Addr)arg[1]; + UInt rzB = arg[2]; + Bool is_zeroed = (Bool)arg[3]; + + MAC_(create_mempool) ( pool, rzB, is_zeroed ); + return True; + } + + case VG_USERREQ__DESTROY_MEMPOOL: { + Addr pool = (Addr)arg[1]; + + MAC_(destroy_mempool) ( pool ); + return True; + } + + case VG_USERREQ__MEMPOOL_ALLOC: { + Addr pool = (Addr)arg[1]; + Addr addr = (Addr)arg[2]; + UInt size = arg[3]; + + MAC_(mempool_alloc) ( pool, addr, size ); + return True; + } + + case VG_USERREQ__MEMPOOL_FREE: { + Addr pool = (Addr)arg[1]; + Addr addr = (Addr)arg[2]; + + MAC_(mempool_free) ( pool, addr ); + return True; + } + default: return False; } diff --git a/memcheck/mac_shared.h b/memcheck/mac_shared.h index c968a08209..1ae789f4f2 100644 --- a/memcheck/mac_shared.h +++ b/memcheck/mac_shared.h @@ -51,7 +51,8 @@ typedef Stack, Unknown, /* classification yielded nothing useful */ Freed, Mallocd, - UserG /* in a user-defined block; Addrcheck & Memcheck only */ + UserG, /* in a user-defined block; Addrcheck & Memcheck only */ + Mempool, /* in a mempool; Addrcheck & Memcheck only */ } AddrKind; @@ -88,7 +89,9 @@ typedef /* Overlapping blocks in memcpy(), strcpy(), etc */ OverlapSupp, /* Something to be suppressed in a leak check. */ - LeakSupp + LeakSupp, + /* Memory pool suppression. */ + MempoolSupp, } MAC_SuppKind; @@ -100,7 +103,8 @@ typedef ParamErr, UserErr, /* behaves like an anonymous ParamErr */ FreeErr, FreeMismatchErr, OverlapErr, - LeakErr + LeakErr, + IllegalMempoolErr, } MAC_ErrorKind; @@ -153,6 +157,18 @@ typedef } MAC_Chunk; +/* Memory pool. Nb: first two fields must match core's VgHashNode. */ +typedef + struct _MAC_Mempool { + struct _MAC_Mempool* next; + Addr pool; /* pool identifier */ + UInt rzB; /* pool red-zone size */ + Bool is_zeroed; /* allocations from this pool are zeroed */ + VgHashTable chunks; /* chunks associated with this pool */ + } + MAC_Mempool; + + /*------------------------------------------------------------*/ /*--- Profiling of tools and memory events ---*/ /*------------------------------------------------------------*/ @@ -270,6 +286,9 @@ extern void MAC_(print_common_debug_usage) ( void ); /* For tracking malloc'd blocks */ extern VgHashTable MAC_(malloc_list); +/* For tracking memory pools. */ +extern VgHashTable MAC_(mempool_list); + /* Function pointers for the two tools to track interesting events. */ extern void (*MAC_(new_mem_heap)) ( Addr a, UInt len, Bool is_inited ); extern void (*MAC_(ban_mem_heap)) ( Addr a, UInt len ); @@ -298,10 +317,16 @@ extern void MAC_(clear_MAC_Error) ( MAC_Error* err_extra ); extern Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su ); -extern void MAC_(new_block) ( Addr p, UInt size, UInt rzB, - Bool is_zeroed, MAC_AllocKind kind ); +extern MAC_Chunk* MAC_(new_block) ( Addr p, UInt size, UInt rzB, + Bool is_zeroed, MAC_AllocKind kind, + VgHashTable table); extern void MAC_(handle_free) ( Addr p, UInt rzB, MAC_AllocKind kind ); +extern void MAC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed); +extern void MAC_(destroy_mempool)(Addr pool); +extern void MAC_(mempool_alloc)(Addr pool, Addr addr, UInt size); +extern void MAC_(mempool_free)(Addr pool, Addr addr); + extern void MAC_(record_address_error) ( ThreadId tid, Addr a, Int size, Bool isWrite ); extern void MAC_(record_core_mem_error) ( ThreadId tid, Bool isWrite, @@ -312,6 +337,7 @@ extern void MAC_(record_jump_error) ( ThreadId tid, Addr a ); extern void MAC_(record_free_error) ( ThreadId tid, Addr a ); extern void MAC_(record_freemismatch_error)( ThreadId tid, Addr a ); extern void MAC_(record_overlap_error) ( Char* function, OverlapExtra* oe ); +extern void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr pool ); extern void MAC_(pp_shared_SkinError) ( Error* err); diff --git a/memcheck/mc_clientreqs.c b/memcheck/mc_clientreqs.c index 9f383f6eaf..9592237741 100644 --- a/memcheck/mc_clientreqs.c +++ b/memcheck/mc_clientreqs.c @@ -134,6 +134,37 @@ Bool MC_(client_perm_maybe_describe)( Addr a, AddrInfo* ai ) if (vg_cgbs[i].kind == CG_NotInUse) continue; if (VG_(addr_is_in_block)(a, vg_cgbs[i].start, vg_cgbs[i].size)) { + MAC_Mempool **d, *mp; + + /* OK - maybe it's a mempool, too? */ + mp = (MAC_Mempool*)VG_(HT_get_node)(MAC_(mempool_list), + (UInt)vg_cgbs[i].start, + (VgHashNode***)&d); + if(mp != NULL) { + if(mp->chunks != NULL) { + MAC_Chunk *mc; + + Bool find_addr(VgHashNode* sh_ch) + { + MAC_Chunk *m = (MAC_Chunk*)sh_ch; + return VG_(addr_is_in_block)(a, m->data, m->size); + } + + mc = (MAC_Chunk*)VG_(HT_first_match)(mp->chunks, find_addr); + if(mc != NULL) { + ai->akind = UserG; + ai->blksize = mc->size; + ai->rwoffset = (Int)(a) - (Int)mc->data; + ai->lastchange = mc->where; + return True; + } + } + ai->akind = Mempool; + ai->blksize = vg_cgbs[i].size; + ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start); + ai->lastchange = vg_cgbs[i].where; + return True; + } ai->akind = UserG; ai->blksize = vg_cgbs[i].size; ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start); @@ -152,7 +183,11 @@ Bool SK_(handle_client_request) ( ThreadId tid, UInt* arg, UInt* ret ) if (!VG_IS_SKIN_USERREQ('M','C',arg[0]) && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0] - && VG_USERREQ__FREELIKE_BLOCK != arg[0]) + && VG_USERREQ__FREELIKE_BLOCK != arg[0] + && VG_USERREQ__CREATE_MEMPOOL != arg[0] + && VG_USERREQ__DESTROY_MEMPOOL != arg[0] + && VG_USERREQ__MEMPOOL_ALLOC != arg[0] + && VG_USERREQ__MEMPOOL_FREE != arg[0]) return False; switch (arg[0]) { diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am index 37a3dbae3a..3d38c0635f 100644 --- a/memcheck/tests/Makefile.am +++ b/memcheck/tests/Makefile.am @@ -43,6 +43,7 @@ EXTRA_DIST = $(noinst_SCRIPTS) \ manuel3.stderr.exp manuel3.vgtest \ memalign_test.stderr.exp memalign_test.vgtest \ memcmptest.stderr.exp memcmptest.stdout.exp memcmptest.vgtest \ + mempool.stderr.exp mempool.vgtest \ mismatches.stderr.exp mismatches.vgtest \ mmaptest.stderr.exp mmaptest.vgtest \ nanoleak.stderr.exp nanoleak.vgtest \ @@ -78,8 +79,8 @@ check_PROGRAMS = \ doublefree error_counts errs1 exitprog execve \ fpeflags fprw fwrite inits inline \ malloc1 malloc2 malloc3 manuel1 manuel2 manuel3 \ - memalign_test memcmptest mmaptest nanoleak new_nothrow null_socket \ - overlap pushfpopf \ + memalign_test memcmptest mempool mmaptest nanoleak new_nothrow \ + null_socket overlap pushfpopf \ realloc1 realloc2 realloc3 sigaltstack signal2 supp1 supp2 suppfree \ trivialleak tronical weirdioctl \ mismatches new_override metadata threadederrno writev zeropage @@ -117,6 +118,7 @@ manuel3_SOURCES = manuel3.c mmaptest_SOURCES = mmaptest.c memalign_test_SOURCES = memalign_test.c memcmptest_SOURCES = memcmptest.c +mempool_SOURCES = mempool.c nanoleak_SOURCES = nanoleak.c null_socket_SOURCES = null_socket.c overlap_SOURCES = overlap.c diff --git a/memcheck/tests/mempool.c b/memcheck/tests/mempool.c new file mode 100644 index 0000000000..f40c1b88ab --- /dev/null +++ b/memcheck/tests/mempool.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +#include "../memcheck.h" + +#define SUPERBLOCK_SIZE 100000 +#define REDZONE_SIZE 8 + +static const int USE_MMAP = 0; + +typedef struct _level_list +{ + struct _level_list *next; + char *where; +} level_list; + +typedef struct _pool { + char *mem; + char *where; + int size, left; + level_list *levels; +} pool; + +pool *make_pool() +{ + pool *p; + + if(USE_MMAP) { + p = (pool *)mmap(0, sizeof(pool), PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + p->where = p->mem = (char *)mmap(NULL, SUPERBLOCK_SIZE, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + } else { + p = (pool *)malloc(sizeof(pool)); + p->where = p->mem = (char *)malloc(SUPERBLOCK_SIZE); + } + + p->size = p->left = SUPERBLOCK_SIZE; + p->levels = NULL; + VALGRIND_MAKE_NOACCESS(p->where, SUPERBLOCK_SIZE); + return p; +} + +void push(pool *p) +{ + level_list *l; + + if(USE_MMAP) + l = (level_list *)mmap(0, sizeof(level_list), + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + else + l = (level_list *)malloc(sizeof(level_list)); + + l->next = p->levels; + l->where = p->where; + VALGRIND_CREATE_MEMPOOL(l->where, REDZONE_SIZE, 0); + p->levels = l; +} + +void pop(pool *p) +{ + level_list *l = p->levels; + p->levels = l->next; + VALGRIND_DESTROY_MEMPOOL(l->where); + VALGRIND_MAKE_NOACCESS(l->where, p->where-l->where); + p->where = l->where; + if(USE_MMAP) + munmap(l, sizeof(level_list)); + else + free(l); +} + +void destroy_pool(pool *p) +{ + level_list *l = p->levels; + + while(l) { + pop(p); + } + if(USE_MMAP) { + munmap(p->mem, SUPERBLOCK_SIZE); + munmap(p, sizeof(pool)); + } else { + free(p->mem); + free(p); + } +} + +char *allocate(pool *p, int size) +{ + char *where; + p->left -= size + (REDZONE_SIZE*2); + where = p->where + REDZONE_SIZE; + p->where += size + (REDZONE_SIZE*2); + VALGRIND_MEMPOOL_ALLOC(p->levels->where, where, size); + return where; +} + +//------------------------------------------------------------------------- +// Rest +//------------------------------------------------------------------------- + +void test(void) +{ + char *x1, *x2, *x3, *x4, *x5; + + pool *p = make_pool(); + + push(p); + + x1 = allocate(p, 10); + x2 = allocate(p, 20); + push(p); + x3 = allocate(p, 10); + x4 = allocate(p, 20); + + *x1 = 'a'; // valid + *x2 = 'b'; // valid + + x1[-1] = 'h'; // invalid + x1[10] = 'i'; // invalid + + pop(p); + + *x3 = 'c'; // invalid + *x4 = 'd'; // invalid + + *x1 = 'e'; // valid + *x2 = 'f'; // valid + + x5 = allocate(p, 10); + + *x5 = 'g'; // valid + + // pop(p); + + // *x5 = 'g'; // invalid + + // destroy_pool(p); +} + +int main(void) +{ + test(); + return 0; +} diff --git a/memcheck/tests/mempool.stderr.exp b/memcheck/tests/mempool.stderr.exp new file mode 100644 index 0000000000..26de5ac3fa --- /dev/null +++ b/memcheck/tests/mempool.stderr.exp @@ -0,0 +1,38 @@ +Invalid write of size 1 + at 0x........: test (mempool.c:124) + by 0x........: main (mempool.c:148) + Address 0x........ is 1 bytes before a block of size 10 client-defined + at 0x........: allocate (mempool.c:99) + by 0x........: test (mempool.c:115) + by 0x........: main (mempool.c:148) + +Invalid write of size 1 + at 0x........: test (mempool.c:125) + by 0x........: main (mempool.c:148) + Address 0x........ is 0 bytes after a block of size 10 client-defined + at 0x........: allocate (mempool.c:99) + by 0x........: test (mempool.c:115) + by 0x........: main (mempool.c:148) + +Invalid write of size 1 + at 0x........: test (mempool.c:129) + by 0x........: main (mempool.c:148) + Address 0x........ is 70 bytes inside a mempool of size 100000 client-defined + at 0x........: make_pool (mempool.c:43) + by 0x........: test (mempool.c:111) + by 0x........: main (mempool.c:148) + +Invalid write of size 1 + at 0x........: test (mempool.c:130) + by 0x........: main (mempool.c:148) + Address 0x........ is 96 bytes inside a mempool of size 100000 client-defined + at 0x........: make_pool (mempool.c:43) + by 0x........: test (mempool.c:111) + by 0x........: main (mempool.c:148) + + +20 bytes in 1 blocks are definitely lost in loss record 2 of 3 + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: make_pool (mempool.c:37) + by 0x........: test (mempool.c:111) + by 0x........: main (mempool.c:148) diff --git a/memcheck/tests/mempool.vgtest b/memcheck/tests/mempool.vgtest new file mode 100644 index 0000000000..bcf4ede6f9 --- /dev/null +++ b/memcheck/tests/mempool.vgtest @@ -0,0 +1,2 @@ +prog: mempool +vgopts: -q --leak-check=yes -- 2.47.2