Refactor the malloc_info code.
malloc_info needs to be per native allocator, but the code treated it
like a global function that doesn't depend on the native memory allocator.
Update malloc debug to dump the actual pointers that it has been tracking.
Test: bionic-unit-tests pass.
Test: malloc debug tests pass.
Test: malloc hook tests pass.
Change-Id: I3b0d4d748489dd84c16d16933479dc8b8d79013e
Merged-In: I3b0d4d748489dd84c16d16933479dc8b8d79013e
(cherry picked from commit a3656a98b10d2a4a6194a5d9705ad9c2cc5877b0)
diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp
index 0961a94..f808d0c 100644
--- a/libc/malloc_debug/Android.bp
+++ b/libc/malloc_debug/Android.bp
@@ -123,6 +123,7 @@
static_libs: [
"libc_malloc_debug",
"libdemangle",
+ "libtinyxml2",
],
shared_libs: [
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index 6e9d24f..5542c1e 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -492,6 +492,17 @@
}
}
+void PointerData::GetAllocList(std::vector<ListInfoType>* list) {
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+
+ if (pointers_.empty()) {
+ return;
+ }
+
+ GetList(list, false);
+}
+
void PointerData::GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size,
size_t* total_memory, size_t* backtrace_size) {
std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index 6955c9a..24ca748 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -132,9 +132,6 @@
void PostForkParent();
void PostForkChild();
- static void GetList(std::vector<ListInfoType>* list, bool only_with_backtrace);
- static void GetUniqueList(std::vector<ListInfoType>* list, bool only_with_backtrace);
-
static size_t AddBacktrace(size_t num_frames);
static void RemoveBacktrace(size_t hash_index);
@@ -151,6 +148,7 @@
static void VerifyFreedPointer(const FreePointerInfoType& info);
static void VerifyAllFreed();
+ static void GetAllocList(std::vector<ListInfoType>* list);
static void LogLeaks();
static void DumpLiveToFile(FILE* fp);
@@ -165,6 +163,9 @@
static std::string GetHashString(uintptr_t* frames, size_t num_frames);
static void LogBacktrace(size_t hash_index);
+ static void GetList(std::vector<ListInfoType>* list, bool only_with_backtrace);
+ static void GetUniqueList(std::vector<ListInfoType>* list, bool only_with_backtrace);
+
size_t alloc_offset_ = 0;
std::vector<uint8_t> cmp_mem_;
diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map
index 2f590d0..8ed37fa 100644
--- a/libc/malloc_debug/exported32.map
+++ b/libc/malloc_debug/exported32.map
@@ -14,6 +14,7 @@
debug_malloc_backtrace;
debug_malloc_disable;
debug_malloc_enable;
+ debug_malloc_info;
debug_malloc_usable_size;
debug_mallopt;
debug_memalign;
diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map
index 08d36a5..cdff88b 100644
--- a/libc/malloc_debug/exported64.map
+++ b/libc/malloc_debug/exported64.map
@@ -14,6 +14,7 @@
debug_malloc_backtrace;
debug_malloc_disable;
debug_malloc_enable;
+ debug_malloc_info;
debug_malloc_usable_size;
debug_mallopt;
debug_memalign;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index f662957..093bdee 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -29,6 +29,7 @@
#include <errno.h>
#include <inttypes.h>
#include <malloc.h>
+#include <stdio.h>
#include <string.h>
#include <sys/cdefs.h>
#include <sys/param.h>
@@ -41,6 +42,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/bionic_malloc_dispatch.h>
+#include <private/MallocXmlElem.h>
#include "Config.h"
#include "DebugData.h"
@@ -85,6 +87,7 @@
void* debug_calloc(size_t nmemb, size_t bytes);
struct mallinfo debug_mallinfo();
int debug_mallopt(int param, int value);
+int debug_malloc_info(int options, FILE* fp);
int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
int debug_iterate(uintptr_t base, size_t size,
void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
@@ -725,6 +728,32 @@
return g_dispatch->mallopt(param, value);
}
+int debug_malloc_info(int options, FILE* fp) {
+ if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
+ return g_dispatch->malloc_info(options, fp);
+ }
+
+ MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
+ std::vector<ListInfoType> list;
+ PointerData::GetAllocList(&list);
+
+ size_t alloc_num = 0;
+ for (size_t i = 0; i < list.size(); i++) {
+ MallocXmlElem alloc(fp, "allocation", "nr=\"%zu\"", alloc_num);
+
+ size_t total = 1;
+ size_t size = list[i].size;
+ while (i < list.size() - 1 && list[i + 1].size == size) {
+ i++;
+ total++;
+ }
+ MallocXmlElem(fp, "size").Contents("%zu", list[i].size);
+ MallocXmlElem(fp, "total").Contents("%zu", total);
+ alloc_num++;
+ }
+ return 0;
+}
+
void* debug_aligned_alloc(size_t alignment, size_t size) {
if (DebugCallsDisabled()) {
return g_dispatch->aligned_alloc(alignment, size);
@@ -741,7 +770,7 @@
return g_dispatch->posix_memalign(memptr, alignment, size);
}
- if (!powerof2(alignment)) {
+ if (alignment < sizeof(void*) || !powerof2(alignment)) {
return EINVAL;
}
int saved_errno = errno;
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index a72db3b..66955db 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -16,6 +16,7 @@
#include <malloc.h>
#include <signal.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
@@ -25,10 +26,13 @@
#include <unistd.h>
#include <algorithm>
+#include <memory>
#include <thread>
#include <vector>
#include <utility>
+#include <tinyxml2.h>
+
#include <gtest/gtest.h>
#include <android-base/file.h>
@@ -62,6 +66,7 @@
struct mallinfo debug_mallinfo();
int debug_mallopt(int, int);
+int debug_malloc_info(int, FILE*);
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
void* debug_pvalloc(size_t);
@@ -136,6 +141,7 @@
nullptr,
mallopt,
aligned_alloc,
+ malloc_info,
};
std::string ShowDiffs(uint8_t* a, uint8_t* b, size_t size) {
@@ -2465,3 +2471,82 @@
pointer[-get_tag_offset()] = tag_value;
}
+TEST_F(MallocDebugTest, malloc_info_no_pointer_tracking) {
+ Init("fill");
+
+ char* buffer;
+ size_t size;
+ FILE* memstream = open_memstream(&buffer, &size);
+ ASSERT_TRUE(memstream != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, memstream));
+ ASSERT_EQ(0, fclose(memstream));
+
+ tinyxml2::XMLDocument doc;
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ auto root = doc.FirstChildElement();
+ ASSERT_TRUE(root != nullptr);
+ ASSERT_STREQ("malloc", root->Name());
+ // Don't care what the underyling implementation says, just that it's
+ // not generated by debug malloc.
+ ASSERT_STRNE("debug-malloc-1", root->Attribute("version"));
+}
+
+TEST_F(MallocDebugTest, malloc_info_with_pointer_tracking) {
+ Init("verify_pointers");
+
+ std::unique_ptr<void, decltype(debug_free)*> ptr1(debug_malloc(1000), debug_free);
+ ASSERT_TRUE(ptr1.get() != nullptr);
+ std::unique_ptr<void, decltype(debug_free)*> ptr2(debug_malloc(1000), debug_free);
+ ASSERT_TRUE(ptr2.get() != nullptr);
+ std::unique_ptr<void, decltype(debug_free)*> ptr3(debug_malloc(500), debug_free);
+ ASSERT_TRUE(ptr3.get() != nullptr);
+ std::unique_ptr<void, decltype(debug_free)*> ptr4(debug_malloc(1200), debug_free);
+ ASSERT_TRUE(ptr4.get() != nullptr);
+
+ char* buffer;
+ size_t size;
+ FILE* memstream = open_memstream(&buffer, &size);
+ ASSERT_TRUE(memstream != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, memstream));
+ ASSERT_EQ(0, fclose(memstream));
+
+ SCOPED_TRACE(testing::Message() << "Output:\n" << buffer);
+
+ tinyxml2::XMLDocument doc;
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ auto root = doc.FirstChildElement();
+ ASSERT_TRUE(root != nullptr);
+ ASSERT_STREQ("malloc", root->Name());
+ ASSERT_STREQ("debug-malloc-1", root->Attribute("version"));
+
+ auto alloc = root->FirstChildElement();
+ ASSERT_TRUE(alloc != nullptr);
+ ASSERT_STREQ("allocation", alloc->Name());
+ int val;
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->QueryIntAttribute("nr", &val));
+ ASSERT_EQ(0, val);
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("size")->QueryIntText(&val));
+ ASSERT_EQ(1200, val);
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
+ ASSERT_EQ(1, val);
+
+ alloc = alloc->NextSiblingElement();
+ ASSERT_TRUE(alloc != nullptr);
+ ASSERT_STREQ("allocation", alloc->Name());
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->QueryIntAttribute("nr", &val));
+ ASSERT_EQ(1, val);
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("size")->QueryIntText(&val));
+ ASSERT_EQ(1000, val);
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
+ ASSERT_EQ(2, val);
+
+ alloc = alloc->NextSiblingElement();
+ ASSERT_TRUE(alloc != nullptr);
+ ASSERT_STREQ("allocation", alloc->Name());
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->QueryIntAttribute("nr", &val));
+ ASSERT_EQ(2, val);
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("size")->QueryIntText(&val));
+ ASSERT_EQ(500, val);
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
+ ASSERT_EQ(1, val);
+}