diff --git a/libc/Android.mk b/libc/Android.mk index 487cbd80e..6beb7b388 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -135,6 +135,7 @@ libc_bionic_src_files := \ bionic/link.cpp \ bionic/locale.cpp \ bionic/lstat.cpp \ + bionic/malloc_info.cpp \ bionic/mbrtoc16.cpp \ bionic/mbrtoc32.cpp \ bionic/mbstate.cpp \ diff --git a/libc/bionic/dlmalloc.c b/libc/bionic/dlmalloc.c index e89c5d1ff..fdb1b2642 100644 --- a/libc/bionic/dlmalloc.c +++ b/libc/bionic/dlmalloc.c @@ -16,6 +16,7 @@ #include "dlmalloc.h" +#include "malloc.h" #include "private/bionic_prctl.h" #include "private/libc_logging.h" @@ -54,3 +55,25 @@ static void* named_anonymous_mmap(size_t length) { prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map, length, "libc_malloc"); return map; } + +// Since dlmalloc isn't the default, we'll leave this unimplemented for now. If +// we decide we need it later, we can fill it in. +size_t __mallinfo_narenas() { + return 0; +} + +size_t __mallinfo_nbins() { + return 0; +} + +struct mallinfo __mallinfo_arena_info(size_t) { + struct mallinfo mi; + memset(&mi, 0, sizeof(mi)); + return mi; +} + +struct mallinfo __mallinfo_bin_info(size_t, size_t) { + struct mallinfo mi; + memset(&mi, 0, sizeof(mi)); + return mi; +} diff --git a/libc/bionic/malloc_info.cpp b/libc/bionic/malloc_info.cpp new file mode 100644 index 000000000..99caedbad --- /dev/null +++ b/libc/bionic/malloc_info.cpp @@ -0,0 +1,94 @@ +/* + * 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; + + DISALLOW_COPY_AND_ASSIGN(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 new file mode 100644 index 000000000..5fffae980 --- /dev/null +++ b/libc/bionic/malloc_info.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef LIBC_BIONIC_MALLOC_INFO_H_ +#define LIBC_BIONIC_MALLOC_INFO_H_ + +#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 + +#endif // LIBC_BIONIC_MALLOC_INFO_H_ diff --git a/libc/include/malloc.h b/libc/include/malloc.h index e6ea27606..cb1dd3bcf 100644 --- a/libc/include/malloc.h +++ b/libc/include/malloc.h @@ -24,6 +24,7 @@ */ #include #include +#include __BEGIN_DECLS @@ -53,6 +54,27 @@ struct mallinfo { extern struct mallinfo mallinfo(void); +/* + * XML structure for malloc_info(3) is in the following format: + * + * + * + * INT + * INT + * INT + * INT + * + * INT + * INT + * INT + * + * + * + * + * + */ +extern int malloc_info(int, FILE *); + __END_DECLS #endif /* LIBC_INCLUDE_MALLOC_H_ */ diff --git a/tests/Android.mk b/tests/Android.mk index bdfd2ef5d..4f1661a82 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -130,6 +130,7 @@ libBionicStandardTests_cppflags := \ libBionicStandardTests_c_includes := \ bionic/libc \ + external/tinyxml2 \ libBionicStandardTests_ldlibs_host := \ -lrt \ @@ -243,6 +244,10 @@ include $(LOCAL_PATH)/Android.build.mk bionic-unit-tests_whole_static_libraries := \ libBionicTests \ +bionic-unit-tests_static_libraries := \ + libtinyxml2 \ + liblog \ + bionic-unit-tests_src_files := \ atexit_test.cpp \ dlext_test.cpp \ @@ -280,6 +285,8 @@ bionic-unit-tests-static_static_libraries := \ libm \ libc \ libstdc++ \ + libtinyxml2 \ + liblog \ bionic-unit-tests-static_force_static_executable := true diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp index 6b7a28b1c..b76625a7a 100644 --- a/tests/malloc_test.cpp +++ b/tests/malloc_test.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include "private/bionic_config.h" TEST(malloc, malloc_std) { @@ -322,3 +324,51 @@ TEST(malloc, valloc_overflow) { ASSERT_EQ(NULL, valloc(SIZE_MAX)); } #endif + +TEST(malloc, malloc_info) { +#ifdef __BIONIC__ + char* buf; + size_t bufsize; + FILE* memstream = open_memstream(&buf, &bufsize); + ASSERT_NE(nullptr, memstream); + ASSERT_EQ(0, malloc_info(0, memstream)); + ASSERT_EQ(0, fclose(memstream)); + + tinyxml2::XMLDocument doc; + ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf)); + + auto root = doc.FirstChildElement(); + ASSERT_NE(nullptr, root); + ASSERT_STREQ("malloc", root->Name()); + ASSERT_STREQ("jemalloc-1", root->Attribute("version")); + + 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)); + + 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)); + } + } + } +#endif +}