From: Simon Glass <sjg@chromium.org>
Heap-pressure tests like common_test_malloc_very_large() assume almost
the whole heap is available as one contiguous free chunk. Even a single,
stray, leaked allocation in the middle of the area breaks that
assumption and fails the test, which makes the tests flakily depend on
every earlier test running with a perfect cleanup.
Add malloc_largest_free() which walks all segments (and the top and dv
chunks) and returns the biggest free chunk size, minus the per-chunk
dlmalloc overhead. Report it from 'malloc info' so the value is easy to
see while diagnosing fragmentation, and check for the new line in
cmd_test_malloc_info()
Signed-off-by: Simon Glass <sjg@chromium.org>
---
cmd/malloc.c | 1 +
common/dlmalloc.c | 29 +++++++++++++++++++++++++++++
include/malloc.h | 13 +++++++++++++
test/cmd/malloc.c | 1 +
4 files changed, 44 insertions(+)
@@ -24,6 +24,7 @@ static int do_malloc_info(struct cmd_tbl *cmdtp, int flag, int argc,
printf("total bytes = %s\n", format_size(buf, info.total_bytes));
printf("in use bytes = %s\n", format_size(buf, info.in_use_bytes));
+ printf("largest free = %s\n", format_size(buf, malloc_largest_free()));
printf("malloc count = %lu\n", info.malloc_count);
printf("free count = %lu\n", info.free_count);
printf("realloc count = %lu\n", info.realloc_count);
@@ -7900,6 +7900,35 @@ void malloc_leak_check_free(struct malloc_leak_snap *snap)
snap->count = 0;
}
+size_t malloc_largest_free(void)
+{
+ size_t largest = 0;
+ mstate m = gm;
+ msegmentptr s;
+
+ if (m->topsize > largest)
+ largest = m->topsize;
+ if (m->dvsize > largest)
+ largest = m->dvsize;
+
+ for (s = &m->seg; s != 0; s = s->next) {
+ mchunkptr q = align_as_chunk(s->base);
+
+ while (segment_holds(s, q) && q != m->top &&
+ q->head != FENCEPOST_HEAD) {
+ if (!is_inuse(q)) {
+ size_t sz = chunksize(q);
+
+ if (sz > largest)
+ largest = sz;
+ }
+ q = next_chunk(q);
+ }
+ }
+
+ return largest > CHUNK_OVERHEAD ? largest - CHUNK_OVERHEAD : 0;
+}
+
int initf_malloc(void)
{
#if CONFIG_IS_ENABLED(SYS_MALLOC_F)
@@ -892,6 +892,19 @@ static inline size_t malloc_mcheck_count(void) { return 0; }
*/
size_t malloc_chunk_size(void *ptr);
+/**
+ * malloc_largest_free() - Return the size of the largest free chunk
+ *
+ * Walks the heap and returns the largest contiguous free region that
+ * malloc() could currently hand out, minus the per-chunk dlmalloc
+ * overhead. Any mcheck header/canary overhead still comes off the top
+ * of the caller's request. Useful for tests that want to allocate "as
+ * much as possible" without tripping over fragmentation.
+ *
+ * Return: approximate largest request bytes malloc() would satisfy
+ */
+size_t malloc_largest_free(void);
+
/**
* malloc_mcheck_hdr_size() - Return the size of the mcheck header
*
@@ -25,6 +25,7 @@ static int cmd_test_malloc_info(struct unit_test_state *uts)
ut_assertok(run_command("malloc info", 0));
ut_assert_nextlinen("total bytes = ");
ut_assert_nextlinen("in use bytes = ");
+ ut_assert_nextlinen("largest free = ");
ut_assert_nextlinen("malloc count = ");
ut_assert_nextlinen("free count = ");
ut_assert_nextlinen("realloc count = ");