diff --git a/libc/Android.bp b/libc/Android.bp index 669ffc06c..0e902c179 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -1089,7 +1089,6 @@ cc_library_static { "bionic/locale.cpp", "bionic/lockf.cpp", "bionic/lstat.cpp", - "bionic/malloc_info.cpp", "bionic/mblen.cpp", "bionic/mbrtoc16.cpp", "bionic/mbrtoc32.cpp", diff --git a/libc/bionic/jemalloc.h b/libc/bionic/jemalloc.h index 3fd36e4e3..b9a4e993c 100644 --- a/libc/bionic/jemalloc.h +++ b/libc/bionic/jemalloc.h @@ -34,6 +34,7 @@ int je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t struct mallinfo je_mallinfo(); void je_malloc_disable(); void je_malloc_enable(); +int je_malloc_info(int options, FILE* fp); int je_mallopt(int, int); void* je_memalign_round_up_boundary(size_t, size_t); void* je_pvalloc(size_t); diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp index c51324606..bc3a9dcdf 100644 --- a/libc/bionic/jemalloc_wrapper.cpp +++ b/libc/bionic/jemalloc_wrapper.cpp @@ -19,8 +19,9 @@ #include #include +#include + #include "jemalloc.h" -#include "private/bionic_macros.h" void* je_pvalloc(size_t bytes) { size_t pagesize = getpagesize(); @@ -116,3 +117,49 @@ int je_mallopt(int param, int value) { } return 0; } + +__BEGIN_DECLS + +size_t __mallinfo_narenas(); +size_t __mallinfo_nbins(); +struct mallinfo __mallinfo_arena_info(size_t); +struct mallinfo __mallinfo_bin_info(size_t, size_t); + +__END_DECLS + +int je_malloc_info(int options, FILE* fp) { + if (options != 0) { + errno = EINVAL; + return -1; + } + + MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\""); + + // Dump all of the large allocations in the arenas. + for (size_t i = 0; i < __mallinfo_narenas(); i++) { + struct mallinfo mi = __mallinfo_arena_info(i); + if (mi.hblkhd != 0) { + MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i); + { + MallocXmlElem(fp, "allocated-large").Contents("%zu", mi.ordblks); + MallocXmlElem(fp, "allocated-huge").Contents("%zu", mi.uordblks); + MallocXmlElem(fp, "allocated-bins").Contents("%zu", mi.fsmblks); + + size_t total = 0; + for (size_t j = 0; j < __mallinfo_nbins(); j++) { + struct mallinfo mi = __mallinfo_bin_info(i, j); + if (mi.ordblks != 0) { + MallocXmlElem bin_elem(fp, "bin", "nr=\"%d\"", j); + MallocXmlElem(fp, "allocated").Contents("%zu", mi.ordblks); + MallocXmlElem(fp, "nmalloc").Contents("%zu", mi.uordblks); + MallocXmlElem(fp, "ndalloc").Contents("%zu", mi.fordblks); + total += mi.ordblks; + } + } + MallocXmlElem(fp, "bins-total").Contents("%zu", total); + } + } + } + + return 0; +} diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index bb8ec59d1..edd697d51 100644 --- a/libc/bionic/malloc_common.cpp +++ b/libc/bionic/malloc_common.cpp @@ -42,6 +42,7 @@ // write_malloc_leak_info: Writes the leak info data to a file. #include +#include #include @@ -93,6 +94,14 @@ extern "C" struct mallinfo mallinfo() { return Malloc(mallinfo)(); } +extern "C" int malloc_info(int options, FILE* fp) { + auto dispatch_table = GetDispatchTable(); + if (__predict_false(dispatch_table != nullptr)) { + return dispatch_table->malloc_info(options, fp); + } + return Malloc(malloc_info)(options, fp); +} + extern "C" int mallopt(int param, int value) { auto dispatch_table = GetDispatchTable(); if (__predict_false(dispatch_table != nullptr)) { diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp index ce3e761e5..965671849 100644 --- a/libc/bionic/malloc_common_dynamic.cpp +++ b/libc/bionic/malloc_common_dynamic.cpp @@ -85,6 +85,7 @@ static constexpr MallocDispatch __libc_malloc_default_dispatch Malloc(malloc_enable), Malloc(mallopt), Malloc(aligned_alloc), + Malloc(malloc_info), }; static constexpr char kHooksSharedLib[] = "libc_malloc_hooks.so"; @@ -146,6 +147,10 @@ static bool InitMallocFunctions(void* impl_handler, MallocDispatch* table, const if (!InitMallocFunction(impl_handler, &table->malloc, prefix, "malloc")) { return false; } + if (!InitMallocFunction(impl_handler, &table->malloc_info, prefix, + "malloc_info")) { + return false; + } if (!InitMallocFunction(impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size")) { return false; diff --git a/libc/bionic/malloc_heapprofd.cpp b/libc/bionic/malloc_heapprofd.cpp index 5129cd711..9cab67abd 100644 --- a/libc/bionic/malloc_heapprofd.cpp +++ b/libc/bionic/malloc_heapprofd.cpp @@ -108,6 +108,7 @@ static constexpr MallocDispatch __heapprofd_init_dispatch Malloc(malloc_enable), Malloc(mallopt), Malloc(aligned_alloc), + Malloc(malloc_info), }; static void MaybeInstallInitHeapprofdHook(int) { diff --git a/libc/bionic/malloc_info.cpp b/libc/bionic/malloc_info.cpp deleted file mode 100644 index 9c8a4bf87..000000000 --- a/libc/bionic/malloc_info.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "malloc_info.h" - -#include -#include "private/bionic_macros.h" - -class __LIBC_HIDDEN__ Elem { -public: - // name must be valid throughout lifetime of the object. - explicit Elem(FILE* fp, const char* name, - const char* attr_fmt = nullptr, ...) { - this->fp = fp; - this->name = name; - - fprintf(fp, "<%s", name); - if (attr_fmt != nullptr) { - va_list args; - va_start(args, attr_fmt); - fputc(' ', fp); - vfprintf(fp, attr_fmt, args); - va_end(args); - } - fputc('>', fp); - } - - ~Elem() noexcept { - fprintf(fp, "", name); - } - - void contents(const char* fmt, ...) { - va_list args; - va_start(args, fmt); - vfprintf(fp, fmt, args); - va_end(args); - } - -private: - FILE* fp; - const char* name; - - BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(Elem); -}; - -int malloc_info(int options, FILE* fp) { - if (options != 0) { - errno = EINVAL; - return -1; - } - - Elem root(fp, "malloc", "version=\"jemalloc-1\""); - - // Dump all of the large allocations in the arenas. - for (size_t i = 0; i < __mallinfo_narenas(); i++) { - struct mallinfo mi = __mallinfo_arena_info(i); - if (mi.hblkhd != 0) { - Elem arena_elem(fp, "heap", "nr=\"%d\"", i); - { - Elem(fp, "allocated-large").contents("%zu", mi.ordblks); - Elem(fp, "allocated-huge").contents("%zu", mi.uordblks); - Elem(fp, "allocated-bins").contents("%zu", mi.fsmblks); - - size_t total = 0; - for (size_t j = 0; j < __mallinfo_nbins(); j++) { - struct mallinfo mi = __mallinfo_bin_info(i, j); - if (mi.ordblks != 0) { - Elem bin_elem(fp, "bin", "nr=\"%d\"", j); - Elem(fp, "allocated").contents("%zu", mi.ordblks); - Elem(fp, "nmalloc").contents("%zu", mi.uordblks); - Elem(fp, "ndalloc").contents("%zu", mi.fordblks); - total += mi.ordblks; - } - } - Elem(fp, "bins-total").contents("%zu", total); - } - } - } - - return 0; -} diff --git a/libc/bionic/malloc_info.h b/libc/bionic/malloc_info.h deleted file mode 100644 index 257fec105..000000000 --- a/libc/bionic/malloc_info.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -__BEGIN_DECLS - -__LIBC_HIDDEN__ size_t __mallinfo_narenas(); -__LIBC_HIDDEN__ size_t __mallinfo_nbins(); -__LIBC_HIDDEN__ struct mallinfo __mallinfo_arena_info(size_t); -__LIBC_HIDDEN__ struct mallinfo __mallinfo_bin_info(size_t, size_t); - -__END_DECLS diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp index 0961a949a..f808d0cdc 100644 --- a/libc/malloc_debug/Android.bp +++ b/libc/malloc_debug/Android.bp @@ -123,6 +123,7 @@ cc_test { static_libs: [ "libc_malloc_debug", "libdemangle", + "libtinyxml2", ], shared_libs: [ diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp index 6e9d24f56..5542c1e9f 100644 --- a/libc/malloc_debug/PointerData.cpp +++ b/libc/malloc_debug/PointerData.cpp @@ -492,6 +492,17 @@ void PointerData::LogLeaks() { } } +void PointerData::GetAllocList(std::vector* list) { + std::lock_guard pointer_guard(pointer_mutex_); + std::lock_guard 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 pointer_guard(pointer_mutex_); diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h index 6955c9a03..24ca7482a 100644 --- a/libc/malloc_debug/PointerData.h +++ b/libc/malloc_debug/PointerData.h @@ -132,9 +132,6 @@ class PointerData : public OptionData { void PostForkParent(); void PostForkChild(); - static void GetList(std::vector* list, bool only_with_backtrace); - static void GetUniqueList(std::vector* list, bool only_with_backtrace); - static size_t AddBacktrace(size_t num_frames); static void RemoveBacktrace(size_t hash_index); @@ -151,6 +148,7 @@ class PointerData : public OptionData { static void VerifyFreedPointer(const FreePointerInfoType& info); static void VerifyAllFreed(); + static void GetAllocList(std::vector* list); static void LogLeaks(); static void DumpLiveToFile(FILE* fp); @@ -165,6 +163,9 @@ class PointerData : public OptionData { static std::string GetHashString(uintptr_t* frames, size_t num_frames); static void LogBacktrace(size_t hash_index); + static void GetList(std::vector* list, bool only_with_backtrace); + static void GetUniqueList(std::vector* list, bool only_with_backtrace); + size_t alloc_offset_ = 0; std::vector cmp_mem_; diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map index 2f590d091..8ed37faa2 100644 --- a/libc/malloc_debug/exported32.map +++ b/libc/malloc_debug/exported32.map @@ -14,6 +14,7 @@ LIBC_MALLOC_DEBUG { 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 08d36a5ca..cdff88ba1 100644 --- a/libc/malloc_debug/exported64.map +++ b/libc/malloc_debug/exported64.map @@ -14,6 +14,7 @@ LIBC_MALLOC_DEBUG { 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 f66295706..093bdee8c 100644 --- a/libc/malloc_debug/malloc_debug.cpp +++ b/libc/malloc_debug/malloc_debug.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include "Config.h" #include "DebugData.h" @@ -85,6 +87,7 @@ void* debug_realloc(void* pointer, size_t bytes); 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 @@ int debug_mallopt(int param, int value) { 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 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 @@ int debug_posix_memalign(void** memptr, size_t alignment, size_t size) { 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 a72db3b91..66955db59 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 #include +#include #include #include #include @@ -25,10 +26,13 @@ #include #include +#include #include #include #include +#include + #include #include @@ -62,6 +66,7 @@ void debug_free_malloc_leak_info(uint8_t*); 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 @@ MallocDispatch MallocDebugTest::dispatch = { nullptr, mallopt, aligned_alloc, + malloc_info, }; std::string ShowDiffs(uint8_t* a, uint8_t* b, size_t size) { @@ -2465,3 +2471,82 @@ TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) { 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 ptr1(debug_malloc(1000), debug_free); + ASSERT_TRUE(ptr1.get() != nullptr); + std::unique_ptr ptr2(debug_malloc(1000), debug_free); + ASSERT_TRUE(ptr2.get() != nullptr); + std::unique_ptr ptr3(debug_malloc(500), debug_free); + ASSERT_TRUE(ptr3.get() != nullptr); + std::unique_ptr 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); +} diff --git a/libc/malloc_hooks/Android.bp b/libc/malloc_hooks/Android.bp index d4b5a2aa1..d119f896d 100644 --- a/libc/malloc_hooks/Android.bp +++ b/libc/malloc_hooks/Android.bp @@ -39,14 +39,6 @@ cc_library { // ============================================================== cc_test { name: "malloc_hooks_unit_tests", - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, srcs: [ "tests/malloc_hooks_tests.cpp", diff --git a/libc/malloc_hooks/exported32.map b/libc/malloc_hooks/exported32.map index bd3e54766..293d9ac15 100644 --- a/libc/malloc_hooks/exported32.map +++ b/libc/malloc_hooks/exported32.map @@ -13,6 +13,7 @@ LIBC_MALLOC_HOOKS { hooks_malloc_backtrace; hooks_malloc_disable; hooks_malloc_enable; + hooks_malloc_info; hooks_malloc_usable_size; hooks_mallopt; hooks_memalign; diff --git a/libc/malloc_hooks/exported64.map b/libc/malloc_hooks/exported64.map index 72465c4be..340106b2e 100644 --- a/libc/malloc_hooks/exported64.map +++ b/libc/malloc_hooks/exported64.map @@ -13,6 +13,7 @@ LIBC_MALLOC_HOOKS { hooks_malloc_backtrace; hooks_malloc_disable; hooks_malloc_enable; + hooks_malloc_info; hooks_malloc_usable_size; hooks_mallopt; hooks_memalign; diff --git a/libc/malloc_hooks/malloc_hooks.cpp b/libc/malloc_hooks/malloc_hooks.cpp index 715a6295b..07b668f08 100644 --- a/libc/malloc_hooks/malloc_hooks.cpp +++ b/libc/malloc_hooks/malloc_hooks.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ ssize_t hooks_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_co void hooks_free_malloc_leak_info(uint8_t* info); size_t hooks_malloc_usable_size(void* pointer); void* hooks_malloc(size_t size); +int hooks_malloc_info(int options, FILE* fp); void hooks_free(void* pointer); void* hooks_memalign(size_t alignment, size_t bytes); void* hooks_aligned_alloc(size_t alignment, size_t bytes); @@ -174,6 +176,10 @@ int hooks_mallopt(int param, int value) { return g_dispatch->mallopt(param, value); } +int hooks_malloc_info(int options, FILE* fp) { + return g_dispatch->malloc_info(options, fp); +} + void* hooks_aligned_alloc(size_t alignment, size_t size) { if (__memalign_hook != nullptr && __memalign_hook != default_memalign_hook) { if (!powerof2(alignment) || (size % alignment) != 0) { @@ -191,7 +197,7 @@ void* hooks_aligned_alloc(size_t alignment, size_t size) { int hooks_posix_memalign(void** memptr, size_t alignment, size_t size) { if (__memalign_hook != nullptr && __memalign_hook != default_memalign_hook) { - if (!powerof2(alignment)) { + if (alignment < sizeof(void*) || !powerof2(alignment)) { return EINVAL; } *memptr = __memalign_hook(alignment, size, __builtin_return_address(0)); diff --git a/libc/private/MallocXmlElem.h b/libc/private/MallocXmlElem.h new file mode 100644 index 000000000..04d3eeea6 --- /dev/null +++ b/libc/private/MallocXmlElem.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +class MallocXmlElem { + public: + // Name must be valid throughout lifetime of the object. + explicit MallocXmlElem(FILE* fp, const char* name, + const char* attr_fmt = nullptr, ...) : fp_(fp), name_(name) { + fprintf(fp, "<%s", name_); + if (attr_fmt != nullptr) { + va_list args; + va_start(args, attr_fmt); + fputc(' ', fp_); + vfprintf(fp_, attr_fmt, args); + va_end(args); + } + fputc('>', fp_); + } + + ~MallocXmlElem() noexcept { + fprintf(fp_, "", name_); + } + + void Contents(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(fp_, fmt, args); + va_end(args); + } + +private: + FILE* fp_; + const char* name_; + + BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(MallocXmlElem); +}; diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h index 0dce03d94..aea3a1c09 100644 --- a/libc/private/bionic_malloc_dispatch.h +++ b/libc/private/bionic_malloc_dispatch.h @@ -31,6 +31,7 @@ #include #include +#include #include // Entry in malloc dispatch table. @@ -38,6 +39,7 @@ typedef void* (*MallocCalloc)(size_t, size_t); typedef void (*MallocFree)(void*); typedef struct mallinfo (*MallocMallinfo)(); typedef void* (*MallocMalloc)(size_t); +typedef int (*MallocMallocInfo)(int, FILE*); typedef size_t (*MallocMallocUsableSize)(const void*); typedef void* (*MallocMemalign)(size_t, size_t); typedef int (*MallocPosixMemalign)(void**, size_t, size_t); @@ -73,6 +75,7 @@ struct MallocDispatch { MallocMallocEnable malloc_enable; MallocMallopt mallopt; MallocAlignedAlloc aligned_alloc; + MallocMallocInfo malloc_info; } __attribute__((aligned(32))); #endif diff --git a/tests/Android.bp b/tests/Android.bp index f9ddd6628..8e03abf05 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -200,7 +200,6 @@ cc_test_library { include_dirs: [ "bionic/libc", - "external/tinyxml2", ], target: { diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp index 658f8bdc9..bc6a37b75 100644 --- a/tests/malloc_test.cpp +++ b/tests/malloc_test.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -348,35 +349,42 @@ TEST(malloc, malloc_info) { auto root = doc.FirstChildElement(); ASSERT_NE(nullptr, root); ASSERT_STREQ("malloc", root->Name()); - ASSERT_STREQ("jemalloc-1", root->Attribute("version")); + if (std::string(root->Attribute("version")) == "jemalloc-1") { + // Verify jemalloc version of this data. + ASSERT_STREQ("jemalloc-1", root->Attribute("version")); - auto arena = root->FirstChildElement(); - for (; arena != nullptr; arena = arena->NextSiblingElement()) { - int val; + auto arena = root->FirstChildElement(); + for (; arena != nullptr; arena = arena->NextSiblingElement()) { + int val; - ASSERT_STREQ("heap", arena->Name()); - ASSERT_EQ(tinyxml2::XML_SUCCESS, arena->QueryIntAttribute("nr", &val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - arena->FirstChildElement("allocated-large")->QueryIntText(&val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - arena->FirstChildElement("allocated-huge")->QueryIntText(&val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - arena->FirstChildElement("allocated-bins")->QueryIntText(&val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - arena->FirstChildElement("bins-total")->QueryIntText(&val)); + ASSERT_STREQ("heap", arena->Name()); + ASSERT_EQ(tinyxml2::XML_SUCCESS, arena->QueryIntAttribute("nr", &val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + arena->FirstChildElement("allocated-large")->QueryIntText(&val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + arena->FirstChildElement("allocated-huge")->QueryIntText(&val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + arena->FirstChildElement("allocated-bins")->QueryIntText(&val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + arena->FirstChildElement("bins-total")->QueryIntText(&val)); - auto bin = arena->FirstChildElement("bin"); - for (; bin != nullptr; bin = bin ->NextSiblingElement()) { - if (strcmp(bin->Name(), "bin") == 0) { - ASSERT_EQ(tinyxml2::XML_SUCCESS, bin->QueryIntAttribute("nr", &val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - bin->FirstChildElement("allocated")->QueryIntText(&val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - bin->FirstChildElement("nmalloc")->QueryIntText(&val)); - ASSERT_EQ(tinyxml2::XML_SUCCESS, - bin->FirstChildElement("ndalloc")->QueryIntText(&val)); + auto bin = arena->FirstChildElement("bin"); + for (; bin != nullptr; bin = bin ->NextSiblingElement()) { + if (strcmp(bin->Name(), "bin") == 0) { + ASSERT_EQ(tinyxml2::XML_SUCCESS, bin->QueryIntAttribute("nr", &val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + bin->FirstChildElement("allocated")->QueryIntText(&val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + bin->FirstChildElement("nmalloc")->QueryIntText(&val)); + ASSERT_EQ(tinyxml2::XML_SUCCESS, + bin->FirstChildElement("ndalloc")->QueryIntText(&val)); + } } } + } else { + // Only verify that this is debug-malloc-1, the malloc debug unit tests + // verify the output. + ASSERT_STREQ("debug-malloc-1", root->Attribute("version")); } #endif }