Merge "Fix allocations escaping malloc debug."
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index 7d04457..ef488ee 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -140,30 +140,32 @@
return -1;
}
- MallocXmlElem root(fp, "malloc", "version=\"jemalloc-1\"");
+ fflush(fp);
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "malloc", "version=\"jemalloc-1\"");
// Dump all of the large allocations in the arenas.
for (size_t i = 0; i < je_mallinfo_narenas(); i++) {
struct mallinfo mi = je_mallinfo_arena_info(i);
if (mi.hblkhd != 0) {
- MallocXmlElem arena_elem(fp, "heap", "nr=\"%d\"", i);
+ MallocXmlElem arena_elem(fd, "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);
+ MallocXmlElem(fd, "allocated-large").Contents("%zu", mi.ordblks);
+ MallocXmlElem(fd, "allocated-huge").Contents("%zu", mi.uordblks);
+ MallocXmlElem(fd, "allocated-bins").Contents("%zu", mi.fsmblks);
size_t total = 0;
for (size_t j = 0; j < je_mallinfo_nbins(); j++) {
struct mallinfo mi = je_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);
+ MallocXmlElem bin_elem(fd, "bin", "nr=\"%d\"", j);
+ MallocXmlElem(fd, "allocated").Contents("%zu", mi.ordblks);
+ MallocXmlElem(fd, "nmalloc").Contents("%zu", mi.uordblks);
+ MallocXmlElem(fd, "ndalloc").Contents("%zu", mi.fordblks);
total += mi.ordblks;
}
}
- MallocXmlElem(fp, "bins-total").Contents("%zu", total);
+ MallocXmlElem(fd, "bins-total").Contents("%zu", total);
}
}
}
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index ec7e42d..b1e28b7 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -554,7 +554,7 @@
return pointers_.count(pointer) != 0;
}
-void PointerData::DumpLiveToFile(FILE* fp) {
+void PointerData::DumpLiveToFile(int fd) {
std::vector<ListInfoType> list;
std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
@@ -566,13 +566,13 @@
total_memory += info.size * info.num_allocations;
}
- fprintf(fp, "Total memory: %zu\n", total_memory);
- fprintf(fp, "Allocation records: %zd\n", list.size());
- fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
- fprintf(fp, "\n");
+ dprintf(fd, "Total memory: %zu\n", total_memory);
+ dprintf(fd, "Allocation records: %zd\n", list.size());
+ dprintf(fd, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
+ dprintf(fd, "\n");
for (const auto& info : list) {
- fprintf(fp, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
+ dprintf(fd, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
info.num_allocations);
FrameInfoType* frame_info = info.frame_info;
if (frame_info != nullptr) {
@@ -580,22 +580,22 @@
if (frame_info->frames[i] == 0) {
break;
}
- fprintf(fp, " %" PRIxPTR, frame_info->frames[i]);
+ dprintf(fd, " %" PRIxPTR, frame_info->frames[i]);
}
}
- fprintf(fp, "\n");
+ dprintf(fd, "\n");
if (info.backtrace_info != nullptr) {
- fprintf(fp, " bt_info");
+ dprintf(fd, " bt_info");
for (const auto& frame : *info.backtrace_info) {
- fprintf(fp, " {");
+ dprintf(fd, " {");
if (frame.map_info != nullptr && !frame.map_info->name.empty()) {
- fprintf(fp, "\"%s\"", frame.map_info->name.c_str());
+ dprintf(fd, "\"%s\"", frame.map_info->name.c_str());
} else {
- fprintf(fp, "\"\"");
+ dprintf(fd, "\"\"");
}
- fprintf(fp, " %" PRIx64, frame.rel_pc);
+ dprintf(fd, " %" PRIx64, frame.rel_pc);
if (frame.function_name.empty()) {
- fprintf(fp, " \"\" 0}");
+ dprintf(fd, " \"\" 0}");
} else {
char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr,
nullptr);
@@ -605,11 +605,11 @@
} else {
name = frame.function_name.c_str();
}
- fprintf(fp, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
+ dprintf(fd, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
free(demangled_name);
}
}
- fprintf(fp, "\n");
+ dprintf(fd, "\n");
}
}
}
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index c7958f3..78f0ed8 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -152,7 +152,7 @@
static void GetAllocList(std::vector<ListInfoType>* list);
static void LogLeaks();
- static void DumpLiveToFile(FILE* fp);
+ static void DumpLiveToFile(int fd);
static void GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
size_t* backtrace_size);
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index c030d54..3c0e630 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -785,16 +785,23 @@
if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
return g_dispatch->malloc_info(options, fp);
}
+
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
- MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
+ // Avoid any issues where allocations are made that will be freed
+ // in the fclose.
+ int fd = fileno(fp);
+ MallocXmlElem root(fd, "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);
+ MallocXmlElem alloc(fd, "allocation", "nr=\"%zu\"", alloc_num);
size_t total = 1;
size_t size = list[i].size;
@@ -802,8 +809,8 @@
i++;
total++;
}
- MallocXmlElem(fp, "size").Contents("%zu", list[i].size);
- MallocXmlElem(fp, "total").Contents("%zu", total);
+ MallocXmlElem(fd, "size").Contents("%zu", list[i].size);
+ MallocXmlElem(fd, "total").Contents("%zu", total);
alloc_num++;
}
return 0;
@@ -905,25 +912,28 @@
static std::mutex g_dump_lock;
-static void write_dump(FILE* fp) {
- fprintf(fp, "Android Native Heap Dump v1.2\n\n");
+static void write_dump(int fd) {
+ dprintf(fd, "Android Native Heap Dump v1.2\n\n");
std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
- fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
+ dprintf(fd, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
- PointerData::DumpLiveToFile(fp);
+ PointerData::DumpLiveToFile(fd);
- fprintf(fp, "MAPS\n");
+ dprintf(fd, "MAPS\n");
std::string content;
if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
- fprintf(fp, "Could not open /proc/self/maps\n");
+ dprintf(fd, "Could not open /proc/self/maps\n");
} else {
- fprintf(fp, "%s", content.c_str());
+ dprintf(fd, "%s", content.c_str());
}
- fprintf(fp, "END\n");
+ dprintf(fd, "END\n");
}
bool debug_write_malloc_leak_info(FILE* fp) {
+ // Make sure any pending output is written to the file.
+ fflush(fp);
+
ScopedConcurrentLock lock;
ScopedDisableDebugCalls disable;
@@ -933,7 +943,8 @@
return false;
}
- write_dump(fp);
+ write_dump(fileno(fp));
+
return true;
}
@@ -943,13 +954,13 @@
std::lock_guard<std::mutex> guard(g_dump_lock);
- FILE* fp = fopen(file_name, "w+e");
- if (fp == nullptr) {
+ int fd = open(file_name, O_RDWR | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0644);
+ if (fd == -1) {
error_log("Unable to create file: %s", file_name);
return;
}
error_log("Dumping to file: %s\n", file_name);
- write_dump(fp);
- fclose(fp);
+ write_dump(fd);
+ close(fd);
}
diff --git a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
index 0716758..67bb8d9 100644
--- a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
@@ -37,6 +37,7 @@
#include <time.h>
#include <unistd.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <log/log.h>
@@ -174,6 +175,7 @@
}
static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
+ std::vector<const char*> no_match_strings = std::vector<const char*>{},
time_t timeout_seconds = kTimeoutSeconds) {
std::string log_str;
time_t start = time(nullptr);
@@ -181,12 +183,18 @@
while (true) {
GetLogStr(pid, &log_str);
found_all = true;
+ // Look for the expected strings.
for (auto str : match_strings) {
if (log_str.find(str) == std::string::npos) {
found_all = false;
break;
}
}
+
+ // Verify the unexpected strings are not present.
+ for (auto str : no_match_strings) {
+ ASSERT_TRUE(log_str.find(str) == std::string::npos) << "Unexpectedly found '" << str << "' in log output:\n" << log_str;
+ }
if (found_all) {
return;
}
@@ -194,7 +202,7 @@
break;
}
}
- ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" + log_str;
+ ASSERT_TRUE(found_all) << "Didn't find expected log output:\n" << log_str;
}
TEST(MallocTests, DISABLED_smoke) {}
@@ -464,3 +472,45 @@
<< "Found crash in log.\nLog message: " << log_str;
}
}
+
+TEST(MallocTests, DISABLED_write_leak_info) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+
+ FILE* fp = fdopen(tf.fd, "w+");
+ if (fp == nullptr) {
+ printf("Unable to create %s\n", tf.path);
+ _exit(1);
+ }
+ tf.release();
+
+ void* ptr = malloc(1000);
+ if (ptr == nullptr) {
+ printf("malloc failed\n");
+ _exit(1);
+ }
+ memset(ptr, 0, 1000);
+
+ android_mallopt(M_WRITE_MALLOC_LEAK_INFO_TO_FILE, fp, sizeof(fp));
+
+ fclose(fp);
+
+ free(ptr);
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_no_header) {
+ pid_t pid;
+ ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace", &pid, 0));
+
+ ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+
+ std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
+
+TEST(MallocDebugSystemTest, write_leak_info_header) {
+ pid_t pid;
+ ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_write_leak_info", "verbose backtrace guard", &pid, 0));
+
+ ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"},
+ std::vector<const char*>{" HAS INVALID TAG ", "USED AFTER FREE ", "UNKNOWN POINTER "}));
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 0238d10..70457b9 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -27,6 +27,8 @@
#include <algorithm>
#include <memory>
+#include <string>
+#include <string_view>
#include <thread>
#include <vector>
#include <utility>
@@ -73,6 +75,9 @@
void* debug_valloc(size_t);
#endif
+bool debug_write_malloc_leak_info(FILE*);
+void debug_dump_heap(const char*);
+
__END_DECLS
constexpr char DIVIDER[] =
@@ -1302,6 +1307,10 @@
}
static std::string SanitizeHeapData(const std::string& data) {
+ if (data.empty()) {
+ return data;
+ }
+
// Remove the map data since it's not consistent.
std::string sanitized;
bool skip_map_data = false;
@@ -1329,7 +1338,7 @@
sanitized += line + '\n';
}
}
- return sanitized;
+ return android::base::Trim(sanitized);
}
void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
@@ -1402,9 +1411,7 @@
z 1 sz 40 num 1 bt 300 400
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1463,9 +1470,7 @@
z 0 sz 300 num 1 bt 100 200
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1513,9 +1518,7 @@
z 0 sz 300 num 2 bt 100 200
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1575,9 +1578,7 @@
bt_info {"" 100 "fake1" a} {"" 200 "fake2" 14}
MAPS
MAP_DATA
-END
-
-)";
+END)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -2472,15 +2473,19 @@
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));
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_TRUE(root != nullptr);
ASSERT_STREQ("malloc", root->Name());
@@ -2501,17 +2506,21 @@
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));
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, debug_malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
- SCOPED_TRACE(testing::Message() << "Output:\n" << buffer);
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+
+ SCOPED_TRACE(testing::Message() << "Output:\n" << contents);
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buffer));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_TRUE(root != nullptr);
ASSERT_STREQ("malloc", root->Name());
@@ -2548,3 +2557,134 @@
ASSERT_EQ(tinyxml2::XML_SUCCESS, alloc->FirstChildElement("total")->QueryIntText(&val));
ASSERT_EQ(1, val);
}
+
+static void AllocPtrsWithBacktrace(std::vector<void*>* ptrs) {
+ backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc});
+ void* ptr = debug_malloc(1024);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 1024);
+ ptrs->push_back(ptr);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002});
+ ptr = debug_malloc(500);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 500);
+ ptrs->push_back(ptr);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0x104});
+ ptr = debug_malloc(100);
+ ASSERT_TRUE(ptr != nullptr);
+ memset(ptr, 0, 100);
+ ptrs->push_back(ptr);
+}
+
+static constexpr std::string_view kDumpInfo = R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
+
+Total memory: 1624
+Allocation records: 3
+Backtrace size: 16
+
+z 0 sz 1024 num 1 bt f e d c
+z 0 sz 500 num 1 bt bc000 bc001 bc002
+z 0 sz 100 num 1 bt 104
+MAPS
+MAP_DATA
+END)";
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ FILE* fp = fopen(tf.path, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+
+ fclose(fp);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected(kDumpInfo);
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, debug_write_malloc_leak_info_extra_data) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ FILE* fp = fopen(tf.path, "w+");
+ ASSERT_TRUE(fp != nullptr);
+
+ fprintf(fp, "This message should appear before the output.\n");
+ ASSERT_TRUE(debug_write_malloc_leak_info(fp));
+ fprintf(fp, "This message should appear after the output.\n");
+
+ fclose(fp);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected = "This message should appear before the output.\n"
+ + std::string(kDumpInfo)
+ + "\nThis message should appear after the output.";
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, dump_heap) {
+ Init("backtrace=16");
+
+ std::vector<void*> ptrs;
+ AllocPtrsWithBacktrace(&ptrs);
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ close(tf.fd);
+ tf.release();
+ debug_dump_heap(tf.path);
+
+ for (auto ptr : ptrs) {
+ debug_free(ptr);
+ }
+ ptrs.clear();
+
+ std::string expected(kDumpInfo);
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
+ contents = SanitizeHeapData(contents);
+ ASSERT_EQ(expected, contents);
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string expected_log = std::string("6 malloc_debug Dumping to file: ") + tf.path + "\n\n";
+ ASSERT_EQ(expected_log, getFakeLogPrint());
+}
diff --git a/libc/private/MallocXmlElem.h b/libc/private/MallocXmlElem.h
index 04d3eee..a367972 100644
--- a/libc/private/MallocXmlElem.h
+++ b/libc/private/MallocXmlElem.h
@@ -18,38 +18,39 @@
#include <stdarg.h>
#include <stdio.h>
+#include <unistd.h>
#include <private/bionic_macros.h>
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_);
+ explicit MallocXmlElem(int fd, const char* name,
+ const char* attr_fmt = nullptr, ...) : fd_(fd), name_(name) {
+ dprintf(fd_, "<%s", name_);
if (attr_fmt != nullptr) {
va_list args;
va_start(args, attr_fmt);
- fputc(' ', fp_);
- vfprintf(fp_, attr_fmt, args);
+ write(fd_, " ", 1);
+ vdprintf(fd_, attr_fmt, args);
va_end(args);
}
- fputc('>', fp_);
+ write(fd_, ">", 1);
}
~MallocXmlElem() noexcept {
- fprintf(fp_, "</%s>", name_);
+ dprintf(fd_, "</%s>", name_);
}
void Contents(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
- vfprintf(fp_, fmt, args);
+ vdprintf(fd_, fmt, args);
va_end(args);
}
private:
- FILE* fp_;
+ int fd_;
const char* name_;
BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(MallocXmlElem);
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 0407553..ebbd247 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -358,15 +358,20 @@
TEST(malloc, malloc_info) {
#ifdef __BIONIC__
SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
- 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));
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
+ ASSERT_EQ(0, malloc_info(0, fp));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
auto root = doc.FirstChildElement();
ASSERT_NE(nullptr, root);
@@ -416,17 +421,21 @@
#ifdef __BIONIC__
SKIP_WITH_HWASAN; // hwasan does not implement malloc_info
- char* buf;
- size_t bufsize;
- FILE* memstream = open_memstream(&buf, &bufsize);
- ASSERT_NE(nullptr, memstream);
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ FILE* fp = fdopen(tf.fd, "w+");
+ tf.release();
+ ASSERT_TRUE(fp != nullptr);
size_t mallinfo_before_allocated_bytes = mallinfo().uordblks;
- ASSERT_EQ(0, malloc_info(0, memstream));
+ ASSERT_EQ(0, malloc_info(0, fp));
size_t mallinfo_after_allocated_bytes = mallinfo().uordblks;
- ASSERT_EQ(0, fclose(memstream));
+ ASSERT_EQ(0, fclose(fp));
+
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(tf.path, &contents));
tinyxml2::XMLDocument doc;
- ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(buf));
+ ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str()));
size_t total_allocated_bytes = 0;
auto root = doc.FirstChildElement();