Refined mallinfo() implementation (contributed by Eugene Toder).
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7901 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_mallocfree.c b/coregrind/m_mallocfree.c
index c1f01f8..f78b18e 100644
--- a/coregrind/m_mallocfree.c
+++ b/coregrind/m_mallocfree.c
@@ -47,6 +47,10 @@
// #define DEBUG_MALLOC // turn on heavyweight debugging machinery
// #define VERBOSE_MALLOC // make verbose, esp. in debugging machinery
+/* Number and total size of blocks in free queue. Used by mallinfo(). */
+Long VG_(free_queue_volume) = 0;
+Long VG_(free_queue_length) = 0;
+
/*------------------------------------------------------------*/
/*--- Main types ---*/
/*------------------------------------------------------------*/
@@ -1468,9 +1472,34 @@
return get_pszB(a, b);
}
-// We cannot return the whole struct as the library function does,
-// because this is called by a client request. So instead we use
-// a pointer to do call by reference.
+
+// Implementation of mallinfo(). There is no recent standard that defines
+// the behavior of mallinfo(). The meaning of the fields in struct mallinfo
+// is as follows:
+//
+// struct mallinfo {
+// int arena; /* total space in arena */
+// int ordblks; /* number of ordinary blocks */
+// int smblks; /* number of small blocks */
+// int hblks; /* number of holding blocks */
+// int hblkhd; /* space in holding block headers */
+// int usmblks; /* space in small blocks in use */
+// int fsmblks; /* space in free small blocks */
+// int uordblks; /* space in ordinary blocks in use */
+// int fordblks; /* space in free ordinary blocks */
+// int keepcost; /* space penalty if keep option */
+// /* is used */
+// };
+//
+// The glibc documentation about mallinfo (which is somewhat outdated) can
+// be found here:
+// http://www.gnu.org/software/libtool/manual/libc/Statistics-of-Malloc.html
+//
+// See also http://bugs.kde.org/show_bug.cgi?id=160956.
+//
+// Regarding the implementation of VG_(mallinfo)(): we cannot return the
+// whole struct as the library function does, because this is called by a
+// client request. So instead we use a pointer to do call by reference.
void VG_(mallinfo) ( ThreadId tid, struct vg_mallinfo* mi )
{
UInt i, free_blocks, free_blocks_size;
@@ -1491,17 +1520,16 @@
}
// We don't have fastbins so smblks & fsmblks are always 0. Also we don't
- // have a separate mmap allocator so set hblks & hblkhd to 0. See also
- // http://www.gnu.org/software/libtool/manual/libc/Statistics-of-Malloc.html
+ // have a separate mmap allocator so set hblks & hblkhd to 0.
mi->arena = a->bytes_mmaped;
- mi->ordblks = free_blocks;
+ mi->ordblks = free_blocks + VG_(free_queue_length);
mi->smblks = 0;
mi->hblks = 0;
mi->hblkhd = 0;
mi->usmblks = 0;
mi->fsmblks = 0;
- mi->uordblks = a->bytes_on_loan;
- mi->fordblks = free_blocks_size;
+ mi->uordblks = a->bytes_on_loan - VG_(free_queue_volume);
+ mi->fordblks = free_blocks_size + VG_(free_queue_volume);
mi->keepcost = 0; // may want some value in here
}
diff --git a/include/pub_tool_replacemalloc.h b/include/pub_tool_replacemalloc.h
index 9b54f20..dd8b23e 100644
--- a/include/pub_tool_replacemalloc.h
+++ b/include/pub_tool_replacemalloc.h
@@ -41,6 +41,13 @@
extern void* VG_(cli_malloc) ( SizeT align, SizeT nbytes );
extern void VG_(cli_free) ( void* p );
+/* If a tool uses deferred freeing (e.g. memcheck to catch accesses to
+ freed memory) it can maintain number and total size of queued blocks
+ in these variable to provide more accurate statistics about client
+ memory usage. Currently used by mallinfo(). */
+extern Long VG_(free_queue_volume);
+extern Long VG_(free_queue_length);
+
/* Check if an address is within a range, allowing for redzones at edges */
extern Bool VG_(addr_is_in_block)( Addr a, Addr start,
SizeT size, SizeT rz_szB );
diff --git a/memcheck/mc_malloc_wrappers.c b/memcheck/mc_malloc_wrappers.c
index 06f568b..0c2eb72 100644
--- a/memcheck/mc_malloc_wrappers.c
+++ b/memcheck/mc_malloc_wrappers.c
@@ -71,7 +71,6 @@
/* Records blocks after freeing. */
static MC_Chunk* freed_list_start = NULL;
static MC_Chunk* freed_list_end = NULL;
-static Long freed_list_volume = 0;
/* Put a shadow chunk on the freed blocks queue, possibly freeing up
some of the oldest blocks in the queue at the same time. */
@@ -83,33 +82,35 @@
if (freed_list_end == NULL) {
tl_assert(freed_list_start == NULL);
freed_list_end = freed_list_start = mc;
- freed_list_volume = (Long)mc->szB;
+ VG_(free_queue_volume) = (Long)mc->szB;
} else {
tl_assert(freed_list_end->next == NULL);
freed_list_end->next = mc;
freed_list_end = mc;
- freed_list_volume += (Long)mc->szB;
+ VG_(free_queue_volume) += (Long)mc->szB;
if (show)
VG_(printf)("mc_freelist: acquire: volume now %lld\n",
- freed_list_volume);
+ VG_(free_queue_volume));
}
+ VG_(free_queue_length)++;
mc->next = NULL;
/* Release enough of the oldest blocks to bring the free queue
volume below vg_clo_freelist_vol. */
- while (freed_list_volume > MC_(clo_freelist_vol)) {
+ while (VG_(free_queue_volume) > MC_(clo_freelist_vol)) {
MC_Chunk* mc1;
tl_assert(freed_list_start != NULL);
tl_assert(freed_list_end != NULL);
mc1 = freed_list_start;
- freed_list_volume -= (Long)mc1->szB;
+ VG_(free_queue_volume) -= (Long)mc1->szB;
+ VG_(free_queue_length)--;
if (show)
VG_(printf)("mc_freelist: discard: volume now %lld\n",
- freed_list_volume);
- tl_assert(freed_list_volume >= 0);
+ VG_(free_queue_volume));
+ tl_assert(VG_(free_queue_volume) >= 0);
if (freed_list_start == freed_list_end) {
freed_list_start = freed_list_end = NULL;