| /* |
| * Copyright (C) 2015 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.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/cdefs.h> |
| #include <sys/mman.h> |
| #include <sys/param.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <thread> |
| #include <vector> |
| #include <utility> |
| |
| #include <tinyxml2.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include <android-base/file.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <android-base/test_utils.h> |
| |
| #include <platform/bionic/macros.h> |
| #include <private/bionic_malloc_dispatch.h> |
| |
| #include <unwindstack/Unwinder.h> |
| |
| #include "Config.h" |
| #include "malloc_debug.h" |
| |
| #include "log_fake.h" |
| #include "backtrace_fake.h" |
| |
| __BEGIN_DECLS |
| |
| bool debug_initialize(const MallocDispatch*, bool*, const char*); |
| void debug_finalize(); |
| |
| void* debug_malloc(size_t); |
| void debug_free(void*); |
| void* debug_calloc(size_t, size_t); |
| void* debug_realloc(void*, size_t); |
| int debug_posix_memalign(void**, size_t, size_t); |
| void* debug_memalign(size_t, size_t); |
| void* debug_aligned_alloc(size_t, size_t); |
| size_t debug_malloc_usable_size(void*); |
| void debug_get_malloc_leak_info(uint8_t**, size_t*, size_t*, size_t*, size_t*); |
| 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); |
| void* debug_valloc(size_t); |
| #endif |
| |
| bool debug_write_malloc_leak_info(FILE*); |
| void debug_dump_heap(const char*); |
| |
| void malloc_enable(); |
| void malloc_disable(); |
| |
| __END_DECLS |
| |
| // Change the slow threshold since some tests take more than 2 seconds. |
| extern "C" bool GetInitialArgs(const char*** args, size_t* num_args) { |
| static const char* initial_args[] = {"--slow_threshold_ms=5000"}; |
| *args = initial_args; |
| *num_args = 1; |
| return true; |
| } |
| |
| constexpr char DIVIDER[] = |
| "6 malloc_debug *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"; |
| |
| static size_t get_tag_offset() { |
| return __BIONIC_ALIGN(sizeof(Header), MINIMUM_ALIGNMENT_BYTES); |
| } |
| |
| static constexpr const char RECORD_ALLOCS_FILE[] = "/data/local/tmp/record_allocs"; |
| |
| static constexpr const char BACKTRACE_DUMP_PREFIX[] = "/data/local/tmp/backtrace_heap"; |
| |
| class MallocDebugTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| initialized = false; |
| resetLogs(); |
| backtrace_fake_clear_all(); |
| } |
| |
| void TearDown() override { |
| if (initialized) { |
| debug_finalize(); |
| } |
| if (!record_filename.empty()) { |
| // Try to delete the record data file even it doesn't exist. |
| unlink(record_filename.c_str()); |
| } |
| } |
| |
| void Init(const char* options) { |
| zygote_child = false; |
| ASSERT_TRUE(debug_initialize(&dispatch, &zygote_child, options)); |
| initialized = true; |
| } |
| |
| void InitRecordAllocs(const char* options) { |
| record_filename = android::base::StringPrintf("%s.%d.txt", RECORD_ALLOCS_FILE, getpid()); |
| std::string init(options); |
| init += android::base::StringPrintf(" record_allocs_file=%s", record_filename.c_str()); |
| Init(init.c_str()); |
| } |
| |
| void BacktraceDumpOnSignal(bool trigger_with_alloc); |
| |
| static size_t GetInfoEntrySize(size_t max_frames) { |
| return 2 * sizeof(size_t) + max_frames * sizeof(uintptr_t); |
| } |
| |
| bool initialized; |
| |
| bool zygote_child; |
| |
| std::string record_filename; |
| |
| static MallocDispatch dispatch; |
| }; |
| |
| MallocDispatch MallocDebugTest::dispatch = { |
| calloc, |
| free, |
| mallinfo, |
| malloc, |
| malloc_usable_size, |
| memalign, |
| posix_memalign, |
| #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) |
| nullptr, |
| #endif |
| realloc, |
| #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) |
| nullptr, |
| #endif |
| nullptr, |
| nullptr, |
| nullptr, |
| mallopt, |
| aligned_alloc, |
| malloc_info, |
| }; |
| |
| std::string ShowDiffs(uint8_t* a, uint8_t* b, size_t size) { |
| std::string diff; |
| for (size_t i = 0; i < size; i++) { |
| if (a[i] != b[i]) { |
| diff += android::base::StringPrintf("Byte %zu: 0x%x 0x%x\n", i, a[i], b[i]); |
| } |
| } |
| return diff; |
| } |
| |
| static void VerifyRecords(std::vector<std::string>& expected, std::string& actual) { |
| ASSERT_TRUE(expected.size() != 0); |
| size_t offset = 0; |
| for (std::string& str : expected) { |
| ASSERT_STREQ(str.c_str(), actual.substr(offset, str.size()).c_str()); |
| if (str.find("thread_done") != std::string::npos) { |
| offset = actual.find_first_of("\n", offset) + 1; |
| continue; |
| } |
| offset += str.size() + 1; |
| uint64_t st = strtoull(&actual[offset], nullptr, 10); |
| offset = actual.find_first_of(" ", offset) + 1; |
| uint64_t et = strtoull(&actual[offset], nullptr, 10); |
| ASSERT_GT(et, st); |
| offset = actual.find_first_of("\n", offset) + 1; |
| } |
| } |
| |
| void VerifyAllocCalls(bool all_options) { |
| size_t alloc_size = 1024; |
| |
| // Verify debug_malloc. |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(alloc_size)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { |
| ASSERT_EQ(0xeb, pointer[i]); |
| } |
| debug_free(pointer); |
| |
| // Verify debug_calloc. |
| pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, alloc_size)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { |
| ASSERT_EQ(0, pointer[i]) << "Failed at byte " << i; |
| } |
| debug_free(pointer); |
| |
| pointer = reinterpret_cast<uint8_t*>(debug_memalign(128, alloc_size)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { |
| ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; |
| } |
| debug_free(pointer); |
| |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, alloc_size)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { |
| ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; |
| } |
| memset(pointer, 0xff, alloc_size); |
| // Increase the size, verify the extra length is initialized to 0xeb, |
| // but the rest is 0xff. |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, alloc_size * 2)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < alloc_size; i++) { |
| ASSERT_EQ(0xff, pointer[i]) << "Failed at byte " << i; |
| } |
| for (size_t i = alloc_size; i < debug_malloc_usable_size(pointer); i++) { |
| ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; |
| } |
| memset(pointer, 0xff, debug_malloc_usable_size(pointer)); |
| // Shrink the size and verify nothing changes. |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, alloc_size)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) { |
| ASSERT_EQ(0xff, pointer[i]) << "Failed at byte " << i; |
| } |
| // This should free the pointer. |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0)); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log; |
| if (all_options) { |
| expected_log += android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to enable backtracing.\n", |
| SIGRTMAX - 19, getpid()); |
| expected_log += android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n", |
| SIGRTMAX - 17, getpid()); |
| expected_log += android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the allocation records.\n", |
| SIGRTMAX - 18, getpid()); |
| expected_log += android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to log allocator stats.\n", |
| SIGRTMAX - 15, getpid()); |
| expected_log += android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to check for unreachable memory.\n", |
| SIGRTMAX - 16, getpid()); |
| } |
| expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, fill_generic) { |
| Init("verbose fill"); |
| VerifyAllocCalls(false); |
| } |
| |
| TEST_F(MallocDebugTest, fill_on_alloc_generic) { |
| Init("verbose fill_on_alloc"); |
| VerifyAllocCalls(false); |
| } |
| |
| TEST_F(MallocDebugTest, fill_on_alloc_partial) { |
| Init("fill_on_alloc=25"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| for (size_t i = 0; i < 25; i++) { |
| ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i; |
| } |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verbose_only) { |
| Init("verbose"); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("4 malloc_debug malloc_testing: malloc debug enabled\n", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verbose_backtrace_enable_on_signal) { |
| Init("verbose backtrace_enable_on_signal"); |
| |
| std::string expected_log = android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to enable backtracing.\n", |
| SIGRTMAX - 19, getpid()); |
| expected_log += android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n", |
| SIGRTMAX - 17, getpid()); |
| expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verbose_backtrace) { |
| Init("verbose backtrace"); |
| |
| std::string expected_log = android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n", |
| SIGRTMAX - 17, getpid()); |
| expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verbose_record_allocs) { |
| Init("verbose record_allocs"); |
| |
| std::string expected_log = android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the allocation records.\n", |
| SIGRTMAX - 18, getpid()); |
| expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verbose_check_unreachable_on_signal) { |
| Init("verbose check_unreachable_on_signal"); |
| |
| std::string expected_log = android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to check for unreachable memory.\n", |
| SIGRTMAX - 16, getpid()); |
| expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verbose_log_allocator_stats_on_signal) { |
| Init("verbose log_allocator_stats_on_signal"); |
| |
| std::string expected_log = android::base::StringPrintf( |
| "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to log allocator stats.\n", SIGRTMAX - 15, |
| getpid()); |
| expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, fill_on_free) { |
| Init("fill_on_free free_track free_track_backtrace_num_frames=0"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| size_t usable_size = debug_malloc_usable_size(pointer); |
| memset(pointer, 0, usable_size); |
| debug_free(pointer); |
| |
| for (size_t i = 0; i < usable_size; i++) { |
| ASSERT_EQ(0xef, pointer[i]) << "Failed at byte " << i; |
| } |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, fill_on_free_partial) { |
| Init("fill_on_free=30 free_track free_track_backtrace_num_frames=0"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| size_t usable_size = debug_malloc_usable_size(pointer); |
| memset(pointer, 0, usable_size); |
| debug_free(pointer); |
| |
| for (size_t i = 0; i < 30; i++) { |
| ASSERT_EQ(0xef, pointer[i]) << "Failed to fill on free at byte " << i; |
| } |
| for (size_t i = 30; i < usable_size; i++) { |
| ASSERT_EQ(0, pointer[i]) << "Filled too much on byte " << i; |
| } |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_partial) { |
| Init("fill_on_free=30 free_track free_track_backtrace_num_frames=0"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| size_t usable_size = debug_malloc_usable_size(pointer); |
| memset(pointer, 0, usable_size); |
| debug_free(pointer); |
| |
| for (size_t i = 0; i < 30; i++) { |
| ASSERT_EQ(0xef, pointer[i]) << "Failed to fill on free at byte " << i; |
| } |
| for (size_t i = 30; i < usable_size; i++) { |
| ASSERT_EQ(0, pointer[i]) << "Filled too much on byte " << i; |
| } |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, all_options) { |
| Init( |
| "guard backtrace backtrace_enable_on_signal fill expand_alloc free_track leak_track " |
| "record_allocs verify_pointers abort_on_error verbose check_unreachable_on_signal " |
| "log_allocator_stats_on_signal log_allocator_stats_on_exit"); |
| VerifyAllocCalls(true); |
| } |
| |
| TEST_F(MallocDebugTest, expand_alloc) { |
| Init("expand_alloc=1024"); |
| |
| void* pointer = debug_malloc(10); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_LE(1034U, debug_malloc_usable_size(pointer)); |
| debug_free(pointer); |
| |
| pointer = debug_calloc(1, 20); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_LE(1044U, debug_malloc_usable_size(pointer)); |
| debug_free(pointer); |
| |
| pointer = debug_memalign(128, 15); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_LE(1039U, debug_malloc_usable_size(pointer)); |
| debug_free(pointer); |
| |
| pointer = debug_aligned_alloc(16, 16); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_LE(1039U, debug_malloc_usable_size(pointer)); |
| debug_free(pointer); |
| |
| pointer = debug_realloc(nullptr, 30); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_LE(1054U, debug_malloc_usable_size(pointer)); |
| pointer = debug_realloc(pointer, 100); |
| ASSERT_LE(1124U, debug_malloc_usable_size(pointer)); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, front_guard) { |
| Init("front_guard=32"); |
| |
| // Create a buffer for doing comparisons. |
| std::vector<uint8_t> buffer(32); |
| memset(buffer.data(), 0xaa, buffer.size()); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size()); |
| memset(pointer, 0xff, 100); |
| debug_free(pointer); |
| |
| // Loop through a bunch alignments. |
| for (size_t alignment = 1; alignment <= 256; alignment++) { |
| pointer = reinterpret_cast<uint8_t*>(debug_memalign(alignment, 100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size()); |
| size_t alignment_mask = alignment - 1; |
| if (!powerof2(alignment)) { |
| alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1; |
| } |
| ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(pointer) & alignment_mask); |
| memset(pointer, 0xff, 100); |
| debug_free(pointer); |
| } |
| |
| pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size()); |
| for (size_t i = 0; i < 100; i++) { |
| ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i; |
| } |
| debug_free(pointer); |
| |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size()); |
| memset(pointer, 0xff, 100); |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200)); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size()); |
| memset(pointer, 0xff, 200); |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0)); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, realloc_memalign_memory) { |
| Init("rear_guard"); |
| |
| void* pointer = debug_memalign(1024, 100); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 100); |
| |
| pointer = debug_realloc(pointer, 1024); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(1024U, debug_malloc_usable_size(pointer)); |
| memset(pointer, 0, 1024); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, front_guard_corrupted) { |
| Init("front_guard=32"); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1, 0x2, 0x3}); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| pointer[-32] = 0x00; |
| pointer[-15] = 0x02; |
| debug_free(pointer); |
| |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED FRONT GUARD\n", pointer); |
| expected_log += "6 malloc_debug allocation[-32] = 0x00 (expected 0xaa)\n"; |
| expected_log += "6 malloc_debug allocation[-15] = 0x02 (expected 0xaa)\n"; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x1\n"; |
| expected_log += "6 malloc_debug #01 pc 0x2\n"; |
| expected_log += "6 malloc_debug #02 pc 0x3\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, rear_guard) { |
| Init("rear_guard=32"); |
| |
| // Create a buffer for doing comparisons. |
| std::vector<uint8_t> buffer(32); |
| memset(buffer.data(), 0xbb, buffer.size()); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[100], buffer.size()); |
| memset(pointer, 0xff, 100); |
| debug_free(pointer); |
| |
| // Loop through a bunch alignments. |
| for (size_t alignment = 1; alignment <= 256; alignment++) { |
| pointer = reinterpret_cast<uint8_t*>(debug_memalign(alignment, 100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[100], buffer.size()); |
| size_t alignment_mask = alignment - 1; |
| if (!powerof2(alignment)) { |
| alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1; |
| } |
| ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(pointer) & alignment_mask) |
| << "Failed at alignment " << alignment << " mask " << alignment_mask; |
| memset(pointer, 0xff, 100); |
| debug_free(pointer); |
| } |
| |
| pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[100], buffer.size()); |
| for (size_t i = 0; i < 100; i++) { |
| ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i; |
| } |
| debug_free(pointer); |
| |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[100], buffer.size()); |
| memset(pointer, 0xff, 100); |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200)); |
| ASSERT_TRUE(memcmp(buffer.data(), &pointer[200], buffer.size()) == 0) |
| << ShowDiffs(buffer.data(), &pointer[200], buffer.size()); |
| for (size_t i = 0; i < 100; i++) { |
| ASSERT_EQ(0xff, pointer[i]) << "debug_realloc not copied byte at " << i; |
| } |
| memset(pointer, 0xff, 200); |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0)); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, rear_guard_corrupted) { |
| Init("rear_guard=32"); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300}); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| pointer[130] = 0xbf; |
| pointer[131] = 0x00; |
| debug_free(pointer); |
| |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED REAR GUARD\n", pointer); |
| expected_log += "6 malloc_debug allocation[130] = 0xbf (expected 0xbb)\n"; |
| expected_log += "6 malloc_debug allocation[131] = 0x00 (expected 0xbb)\n"; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x100\n"; |
| expected_log += "6 malloc_debug #01 pc 0x200\n"; |
| expected_log += "6 malloc_debug #02 pc 0x300\n"; |
| expected_log += DIVIDER; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, rear_guard_corrupted_after_realloc_shrink) { |
| Init("rear_guard=32"); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300}); |
| |
| void* pointer = debug_malloc(200); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 200); |
| |
| uint8_t* pointer_shrink = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 100)); |
| pointer_shrink[130] = 0xbf; |
| pointer_shrink[131] = 0x00; |
| debug_free(pointer); |
| |
| // When shrinking sizes, the same pointer should be returned. |
| ASSERT_EQ(pointer, pointer_shrink); |
| |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED REAR GUARD\n", pointer); |
| expected_log += "6 malloc_debug allocation[130] = 0xbf (expected 0xbb)\n"; |
| expected_log += "6 malloc_debug allocation[131] = 0x00 (expected 0xbb)\n"; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x100\n"; |
| expected_log += "6 malloc_debug #01 pc 0x200\n"; |
| expected_log += "6 malloc_debug #02 pc 0x300\n"; |
| expected_log += DIVIDER; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, tag_corrupted) { |
| Init("rear_guard=32"); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xa, 0xb, 0xc}); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xaa, 0xbb, 0xcc}); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xaaa, 0xbbb, 0xccc}); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| uint8_t saved = pointer[-get_tag_offset()]; |
| pointer[-get_tag_offset()] = 0x00; |
| ASSERT_EQ(0U, debug_malloc_usable_size(pointer)); |
| ASSERT_TRUE(debug_realloc(pointer, 200) == nullptr); |
| debug_free(pointer); |
| |
| // Fix the pointer and really free it. |
| pointer[-get_tag_offset()] = saved; |
| debug_free(pointer); |
| |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (malloc_usable_size)\n", |
| pointer); |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xa\n"; |
| expected_log += "6 malloc_debug #01 pc 0xb\n"; |
| expected_log += "6 malloc_debug #02 pc 0xc\n"; |
| expected_log += DIVIDER; |
| |
| expected_log += DIVIDER; |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (realloc)\n", |
| pointer); |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xaa\n"; |
| expected_log += "6 malloc_debug #01 pc 0xbb\n"; |
| expected_log += "6 malloc_debug #02 pc 0xcc\n"; |
| expected_log += DIVIDER; |
| |
| expected_log += DIVIDER; |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (free)\n", |
| pointer); |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xaaa\n"; |
| expected_log += "6 malloc_debug #01 pc 0xbbb\n"; |
| expected_log += "6 malloc_debug #02 pc 0xccc\n"; |
| expected_log += DIVIDER; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, leak_track_no_frees) { |
| Init("leak_track"); |
| |
| void* pointer1 = debug_malloc(200); |
| ASSERT_TRUE(pointer1 != nullptr); |
| memset(pointer1, 0, 200); |
| |
| void* pointer2 = debug_malloc(128); |
| ASSERT_TRUE(pointer2 != nullptr); |
| memset(pointer2, 0, 128); |
| |
| void* pointer3 = debug_malloc(1024); |
| ASSERT_TRUE(pointer3 != nullptr); |
| memset(pointer3, 0, 1024); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1024 at %p (leak 1 of 3)\n", |
| pointer3); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 200 at %p (leak 2 of 3)\n", |
| pointer1); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 128 at %p (leak 3 of 3)\n", |
| pointer2); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, leak_track_no_frees_with_backtrace) { |
| Init("leak_track backtrace"); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1000, 0x2000, 0x3000}); |
| |
| void* pointer1 = debug_malloc(100); |
| ASSERT_TRUE(pointer1 != nullptr); |
| memset(pointer1, 0, 100); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000, 0xd000}); |
| |
| void* pointer2 = debug_malloc(128); |
| ASSERT_TRUE(pointer2 != nullptr); |
| memset(pointer2, 0, 128); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000}); |
| |
| void* pointer3 = debug_malloc(1024); |
| ASSERT_TRUE(pointer3 != nullptr); |
| memset(pointer3, 0, 1024); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1024 at %p (leak 1 of 3)\n", |
| pointer3); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xfe000\n"; |
| expected_log += "6 malloc_debug #01 pc 0xde000\n"; |
| expected_log += "6 malloc_debug #02 pc 0xce000\n"; |
| expected_log += "6 malloc_debug #03 pc 0xbe000\n"; |
| expected_log += "6 malloc_debug #04 pc 0xae000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 128 at %p (leak 2 of 3)\n", |
| pointer2); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xa000\n"; |
| expected_log += "6 malloc_debug #01 pc 0xb000\n"; |
| expected_log += "6 malloc_debug #02 pc 0xc000\n"; |
| expected_log += "6 malloc_debug #03 pc 0xd000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 100 at %p (leak 3 of 3)\n", |
| pointer1); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x1000\n"; |
| expected_log += "6 malloc_debug #01 pc 0x2000\n"; |
| expected_log += "6 malloc_debug #02 pc 0x3000\n"; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, leak_track_frees) { |
| Init("leak_track"); |
| |
| void* pointer1 = debug_malloc(390); |
| ASSERT_TRUE(pointer1 != nullptr); |
| memset(pointer1, 0, 390); |
| debug_free(pointer1); |
| |
| pointer1 = debug_malloc(100); |
| ASSERT_TRUE(pointer1 != nullptr); |
| memset(pointer1, 0, 100); |
| |
| void* pointer2 = debug_malloc(250); |
| ASSERT_TRUE(pointer2 != nullptr); |
| memset(pointer2, 0, 250); |
| debug_free(pointer2); |
| |
| pointer2 = debug_malloc(450); |
| ASSERT_TRUE(pointer2 != nullptr); |
| memset(pointer2, 0, 450); |
| |
| void* pointer3 = debug_malloc(999); |
| ASSERT_TRUE(pointer3 != nullptr); |
| memset(pointer3, 0, 999); |
| debug_free(pointer2); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 999 at %p (leak 1 of 2)\n", |
| pointer3); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 100 at %p (leak 2 of 2)\n", |
| pointer1); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track) { |
| Init("free_track=5 free_track_backtrace_num_frames=0"); |
| |
| void* pointers[10]; |
| for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { |
| pointers[i] = debug_malloc(100 + i); |
| ASSERT_TRUE(pointers[i] != nullptr); |
| memset(pointers[i], 0, 100 + i); |
| debug_free(pointers[i]); |
| } |
| |
| // Large allocations (> 4096) to verify large allocation checks. |
| void* pointer = debug_malloc(8192); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 8192); |
| debug_free(pointer); |
| |
| pointer = debug_malloc(9000); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 9000); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_use_after_free) { |
| Init("free_track=5 free_track_backtrace_num_frames=0"); |
| |
| uint8_t* pointers[5]; |
| for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { |
| pointers[i] = reinterpret_cast<uint8_t*>(debug_malloc(100 + i)); |
| ASSERT_TRUE(pointers[i] != nullptr); |
| memset(pointers[i], 0, 100 + i); |
| debug_free(pointers[i]); |
| } |
| |
| // Stomp on the data. |
| pointers[0][20] = 0xaf; |
| pointers[0][99] = 0x12; |
| |
| pointers[3][3] = 0x34; |
| |
| // Large allocations (> 4096) to verify large allocation checks. |
| uint8_t* pointer1_large = reinterpret_cast<uint8_t*>(debug_malloc(8192)); |
| ASSERT_TRUE(pointer1_large != nullptr); |
| memset(pointer1_large, 0, 8192); |
| debug_free(pointer1_large); |
| |
| pointer1_large[4095] = 0x90; |
| pointer1_large[4100] = 0x56; |
| pointer1_large[8191] = 0x89; |
| |
| uint8_t* pointer2_large = reinterpret_cast<uint8_t*>(debug_malloc(9000)); |
| ASSERT_TRUE(pointer2_large != nullptr); |
| memset(pointer2_large, 0, 9000); |
| debug_free(pointer2_large); |
| |
| pointer2_large[8200] = 0x78; |
| |
| // Do a bunch of alloc and free to verify the above frees are checked. |
| for (size_t i = 0; i < 10; i++) { |
| void* flush_pointer = debug_malloc(100+i); |
| ASSERT_TRUE(flush_pointer != nullptr); |
| memset(flush_pointer, 0, 100 + i); |
| debug_free(flush_pointer); |
| } |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointers[0]); |
| expected_log += "6 malloc_debug allocation[20] = 0xaf (expected 0xef)\n"; |
| expected_log += "6 malloc_debug allocation[99] = 0x12 (expected 0xef)\n"; |
| expected_log += DIVIDER; |
| expected_log += DIVIDER; |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointers[3]); |
| expected_log += "6 malloc_debug allocation[3] = 0x34 (expected 0xef)\n"; |
| expected_log += DIVIDER; |
| expected_log += DIVIDER; |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer1_large); |
| expected_log += "6 malloc_debug allocation[4095] = 0x90 (expected 0xef)\n"; |
| expected_log += "6 malloc_debug allocation[4100] = 0x56 (expected 0xef)\n"; |
| expected_log += "6 malloc_debug allocation[8191] = 0x89 (expected 0xef)\n"; |
| expected_log += DIVIDER; |
| expected_log += DIVIDER; |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer2_large); |
| expected_log += "6 malloc_debug allocation[8200] = 0x78 (expected 0xef)\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_use_after_free_finalize) { |
| Init("free_track=100 free_track_backtrace_num_frames=0"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 100); |
| debug_free(pointer); |
| |
| pointer[56] = 0x91; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer); |
| expected_log += "6 malloc_debug allocation[56] = 0x91 (expected 0xef)\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) { |
| Init("free_track=100 rear_guard"); |
| |
| // Free backtrace. |
| backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(200)); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 200); |
| debug_free(pointer); |
| |
| pointer[101] = 0xab; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer); |
| expected_log += "6 malloc_debug allocation[101] = 0xab (expected 0xef)\n"; |
| expected_log += "6 malloc_debug Backtrace at time of free:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xfa\n"; |
| expected_log += "6 malloc_debug #01 pc 0xeb\n"; |
| expected_log += "6 malloc_debug #02 pc 0xdc\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_use_after_free_call_realloc) { |
| Init("free_track=100 rear_guard"); |
| |
| // Free backtrace. |
| backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); |
| // Backtrace at realloc. |
| backtrace_fake_add(std::vector<uintptr_t> {0x12, 0x22, 0x32, 0x42}); |
| |
| void* pointer = debug_malloc(200); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 200); |
| debug_free(pointer); |
| |
| // Choose a size that should not trigger a realloc to verify tag is |
| // verified early. |
| ASSERT_TRUE(debug_realloc(pointer, 200) == nullptr); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p USED AFTER FREE (realloc)\n", pointer); |
| expected_log += "6 malloc_debug Backtrace of original free:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xfa\n"; |
| expected_log += "6 malloc_debug #01 pc 0xeb\n"; |
| expected_log += "6 malloc_debug #02 pc 0xdc\n"; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x12\n"; |
| expected_log += "6 malloc_debug #01 pc 0x22\n"; |
| expected_log += "6 malloc_debug #02 pc 0x32\n"; |
| expected_log += "6 malloc_debug #03 pc 0x42\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_use_after_free_call_free) { |
| Init("free_track=100 rear_guard"); |
| |
| // Free backtrace. |
| backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc}); |
| // Backtrace at second free. |
| backtrace_fake_add(std::vector<uintptr_t> {0x12, 0x22, 0x32, 0x42}); |
| |
| void* pointer = debug_malloc(200); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 200); |
| debug_free(pointer); |
| |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p USED AFTER FREE (free)\n", pointer); |
| expected_log += "6 malloc_debug Backtrace of original free:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xfa\n"; |
| expected_log += "6 malloc_debug #01 pc 0xeb\n"; |
| expected_log += "6 malloc_debug #02 pc 0xdc\n"; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x12\n"; |
| expected_log += "6 malloc_debug #01 pc 0x22\n"; |
| expected_log += "6 malloc_debug #02 pc 0x32\n"; |
| expected_log += "6 malloc_debug #03 pc 0x42\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_header_tag_corrupted) { |
| Init("free_track=100 free_track_backtrace_num_frames=0 rear_guard"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 100); |
| debug_free(pointer); |
| |
| pointer[-get_tag_offset()] = 0x00; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ ALLOCATION %p HAS CORRUPTED HEADER TAG 0x1cc7dc00 AFTER FREE\n", |
| pointer); |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_multiple_thread) { |
| Init("free_track=10 free_track_backtrace_num_frames=0"); |
| |
| std::vector<std::thread*> threads(1000); |
| for (size_t i = 0; i < threads.size(); i++) { |
| threads[i] = new std::thread([](){ |
| for (size_t j = 0; j < 100; j++) { |
| void* mem = debug_malloc(100); |
| write(0, mem, 0); |
| debug_free(mem); |
| } |
| }); |
| } |
| for (size_t i = 0; i < threads.size(); i++) { |
| threads[i]->join(); |
| delete threads[i]; |
| } |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, free_track_pointer_modified_after_free) { |
| Init("free_track=4 fill_on_free=2 free_track_backtrace_num_frames=0"); |
| |
| void* pointers[5]; |
| for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) { |
| pointers[i] = debug_malloc(100); |
| ASSERT_TRUE(pointers[i] != nullptr); |
| memset(pointers[i], 0, 100); |
| } |
| |
| debug_free(pointers[0]); |
| |
| // overwrite the whole pointer, only expect errors on the fill bytes we check. |
| memset(pointers[0], 0x20, 100); |
| |
| for (size_t i = 1; i < sizeof(pointers) / sizeof(void*); i++) { |
| debug_free(pointers[i]); |
| } |
| |
| std::string expected_log(DIVIDER); |
| expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", |
| pointers[0]); |
| expected_log += "6 malloc_debug allocation[0] = 0x20 (expected 0xef)\n"; |
| expected_log += "6 malloc_debug allocation[1] = 0x20 (expected 0xef)\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, get_malloc_leak_info_invalid) { |
| Init("fill"); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| std::string expected_log("6 malloc_debug get_malloc_leak_info: At least one invalid parameter.\n"); |
| |
| debug_get_malloc_leak_info(nullptr, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| |
| resetLogs(); |
| debug_get_malloc_leak_info(&info, nullptr, &info_size, &total_memory, &backtrace_size); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| |
| resetLogs(); |
| debug_get_malloc_leak_info(&info, &overall_size, nullptr, &total_memory, &backtrace_size); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| |
| resetLogs(); |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, nullptr, &backtrace_size); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| |
| resetLogs(); |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, nullptr); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, get_malloc_leak_info_not_enabled) { |
| Init("fill"); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| std::string expected_log( |
| "6 malloc_debug get_malloc_leak_info: Allocations not being tracked, to enable " |
| "set the option 'backtrace'.\n"); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| struct InfoEntry { |
| size_t size; |
| size_t num_allocations; |
| uintptr_t frames[0]; |
| } __attribute__((packed)); |
| |
| TEST_F(MallocDebugTest, get_malloc_leak_info_empty) { |
| Init("backtrace"); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info == nullptr); |
| ASSERT_EQ(0U, overall_size); |
| ASSERT_EQ(0U, info_size); |
| ASSERT_EQ(0U, total_memory); |
| ASSERT_EQ(0U, backtrace_size); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, get_malloc_leak_info_single) { |
| Init("backtrace"); |
| |
| // Create the expected info buffer. |
| size_t individual_size = GetInfoEntrySize(16); |
| std::vector<uint8_t> expected_info(individual_size); |
| memset(expected_info.data(), 0, individual_size); |
| |
| InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data()); |
| entry->size = 200; |
| entry->num_allocations = 1; |
| entry->frames[0] = 0xf; |
| entry->frames[1] = 0xe; |
| entry->frames[2] = 0xd; |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd}); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(entry->size)); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, entry->size); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size, overall_size); |
| ASSERT_EQ(individual_size, info_size); |
| ASSERT_EQ(200U, total_memory); |
| ASSERT_EQ(16U, backtrace_size); |
| ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0) |
| << ShowDiffs(expected_info.data(), info, overall_size); |
| |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, get_malloc_leak_info_multi) { |
| Init("backtrace=16"); |
| |
| // Create the expected info buffer. |
| size_t individual_size = GetInfoEntrySize(16); |
| std::vector<uint8_t> expected_info(individual_size * 3); |
| memset(expected_info.data(), 0, individual_size * 3); |
| |
| InfoEntry* entry0 = reinterpret_cast<InfoEntry*>(expected_info.data()); |
| InfoEntry* entry1 = reinterpret_cast<InfoEntry*>( |
| reinterpret_cast<uintptr_t>(entry0) + individual_size); |
| InfoEntry* entry2 = reinterpret_cast<InfoEntry*>( |
| reinterpret_cast<uintptr_t>(entry1) + individual_size); |
| |
| // These values will be in the reverse order that we create. |
| entry2->size = 500; |
| entry2->num_allocations = 1; |
| entry2->frames[0] = 0xf; |
| entry2->frames[1] = 0xe; |
| entry2->frames[2] = 0xd; |
| entry2->frames[3] = 0xc; |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc}); |
| |
| uint8_t* pointers[3]; |
| |
| pointers[0] = reinterpret_cast<uint8_t*>(debug_malloc(entry2->size)); |
| ASSERT_TRUE(pointers[0] != nullptr); |
| memset(pointers[0], 0, entry2->size); |
| |
| entry1->size = 4100; |
| entry1->num_allocations = 1; |
| for (size_t i = 0; i < 16; i++) { |
| entry1->frames[i] = 0xbc000 + i; |
| } |
| |
| backtrace_fake_add( |
| std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002, 0xbc003, 0xbc004, 0xbc005, |
| 0xbc006, 0xbc007, 0xbc008, 0xbc009, 0xbc00a, 0xbc00b, |
| 0xbc00c, 0xbc00d, 0xbc00e, 0xbc00f, 0xffff}); |
| |
| pointers[1] = reinterpret_cast<uint8_t*>(debug_malloc(entry1->size)); |
| ASSERT_TRUE(pointers[1] != nullptr); |
| memset(pointers[1], 0, entry1->size); |
| |
| entry0->size = 9000; |
| entry0->num_allocations = 1; |
| |
| entry0->frames[0] = 0x104; |
| backtrace_fake_add(std::vector<uintptr_t> {0x104}); |
| |
| pointers[2] = reinterpret_cast<uint8_t*>(debug_malloc(entry0->size)); |
| ASSERT_TRUE(pointers[2] != nullptr); |
| memset(pointers[2], 0, entry0->size); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size * 3, overall_size); |
| ASSERT_EQ(individual_size, info_size); |
| ASSERT_EQ(500U + 4100U + 9000U, total_memory); |
| ASSERT_EQ(16U, backtrace_size); |
| ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0) |
| << ShowDiffs(expected_info.data(), info, overall_size); |
| |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointers[0]); |
| debug_free(pointers[1]); |
| debug_free(pointers[2]); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, get_malloc_backtrace_with_header) { |
| Init("backtrace=16 guard"); |
| |
| void* pointer = debug_malloc(100); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 100); |
| EXPECT_EQ(100U, debug_malloc_usable_size(pointer)); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| EXPECT_TRUE(info != nullptr); |
| EXPECT_EQ(GetInfoEntrySize(16), overall_size); |
| EXPECT_EQ(GetInfoEntrySize(16), info_size); |
| EXPECT_EQ(100U, total_memory); |
| EXPECT_EQ(16U, backtrace_size); |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointer); |
| |
| // There should be no pointers that have leaked. |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| 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; |
| bool map_data_found = false; |
| for (auto& line : android::base::Split(data, "\n")) { |
| if (skip_map_data) { |
| if (line == "END") { |
| if (map_data_found) { |
| sanitized += "MAP_DATA\n"; |
| map_data_found = false; |
| } |
| skip_map_data = false; |
| } else { |
| map_data_found = true; |
| continue; |
| } |
| } |
| |
| if (android::base::StartsWith(line, "Build fingerprint:")) { |
| sanitized += "Build fingerprint: ''\n"; |
| } else { |
| if (line == "MAPS") { |
| skip_map_data = true; |
| } |
| sanitized += line + '\n'; |
| } |
| } |
| return android::base::Trim(sanitized); |
| } |
| |
| void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) { |
| Init("backtrace=4"); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x300, 0x400}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x500, 0x600}); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xa100, 0xb200}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xa300, 0xb300}); |
| |
| std::vector<void*> pointers; |
| zygote_child = true; |
| pointers.push_back(debug_malloc(100)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| pointers.push_back(debug_malloc(40)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| pointers.push_back(debug_malloc(200)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| |
| zygote_child = false; |
| pointers.push_back(debug_malloc(10)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| pointers.push_back(debug_malloc(50)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| pointers.push_back(debug_malloc(5)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| |
| // Dump all of the data accumulated so far. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 17) == 0); |
| sleep(1); |
| |
| // This triggers the dumping. |
| if (trigger_with_alloc) { |
| pointers.push_back(debug_malloc(23)); |
| ASSERT_TRUE(pointers.back() != nullptr); |
| } else { |
| debug_free(pointers.back()); |
| pointers.pop_back(); |
| } |
| |
| for (auto* pointer : pointers) { |
| debug_free(pointer); |
| } |
| |
| // Read all of the contents. |
| std::string actual; |
| std::string name = android::base::StringPrintf("%s.%d.txt", BACKTRACE_DUMP_PREFIX, getpid()); |
| ASSERT_TRUE(android::base::ReadFileToString(name, &actual)); |
| ASSERT_EQ(0, unlink(name.c_str())); |
| |
| std::string sanitized(SanitizeHeapData(actual)); |
| |
| std::string expected = |
| R"(Android Native Heap Dump v1.2 |
| |
| Build fingerprint: '' |
| |
| Total memory: 405 |
| Allocation records: 6 |
| Backtrace size: 4 |
| |
| z 0 sz 50 num 1 bt a100 b200 |
| z 0 sz 10 num 1 bt a000 b000 |
| z 0 sz 5 num 1 bt a300 b300 |
| z 1 sz 200 num 1 bt 500 600 |
| z 1 sz 100 num 1 bt 100 200 |
| z 1 sz 40 num 1 bt 300 400 |
| MAPS |
| MAP_DATA |
| END)"; |
| ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug Dumping to file: /data/local/tmp/backtrace_heap.%d.txt\n\n", getpid()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_dump_on_signal_by_malloc) { |
| BacktraceDumpOnSignal(true); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_dump_on_signal_by_free) { |
| BacktraceDumpOnSignal(false); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_dump_on_exit) { |
| pid_t pid; |
| if ((pid = fork()) == 0) { |
| Init("backtrace=4 backtrace_dump_on_exit"); |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000}); |
| |
| std::vector<void*> pointers; |
| pointers.push_back(debug_malloc(300)); |
| pointers.push_back(debug_malloc(400)); |
| pointers.push_back(debug_malloc(500)); |
| |
| // Call the exit function manually. |
| debug_finalize(); |
| _exit(0); |
| } |
| ASSERT_NE(-1, pid); |
| ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0))); |
| |
| // Read all of the contents. |
| std::string actual; |
| std::string name = android::base::StringPrintf("%s.%d.exit.txt", BACKTRACE_DUMP_PREFIX, pid); |
| ASSERT_TRUE(android::base::ReadFileToString(name, &actual)); |
| ASSERT_EQ(0, unlink(name.c_str())); |
| |
| std::string sanitized(SanitizeHeapData(actual)); |
| |
| std::string expected = |
| R"(Android Native Heap Dump v1.2 |
| |
| Build fingerprint: '' |
| |
| Total memory: 1200 |
| Allocation records: 3 |
| Backtrace size: 4 |
| |
| z 0 sz 500 num 1 bt a000 b000 c000 |
| z 0 sz 400 num 1 bt a000 b000 |
| z 0 sz 300 num 1 bt 100 200 |
| MAPS |
| MAP_DATA |
| END)"; |
| ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_dump_on_exit_shared_backtrace) { |
| pid_t pid; |
| if ((pid = fork()) == 0) { |
| Init("backtrace=4 backtrace_dump_on_exit"); |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200}); |
| |
| std::vector<void*> pointers; |
| pointers.push_back(debug_malloc(300)); |
| pointers.push_back(debug_malloc(400)); |
| pointers.push_back(debug_malloc(300)); |
| |
| // Call the exit function manually. |
| debug_finalize(); |
| _exit(0); |
| } |
| ASSERT_NE(-1, pid); |
| ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0))); |
| |
| // Read all of the contents. |
| std::string actual; |
| std::string name = android::base::StringPrintf("%s.%d.exit.txt", BACKTRACE_DUMP_PREFIX, pid); |
| ASSERT_TRUE(android::base::ReadFileToString(name, &actual)); |
| ASSERT_EQ(0, unlink(name.c_str())); |
| |
| std::string sanitized(SanitizeHeapData(actual)); |
| |
| std::string expected = |
| R"(Android Native Heap Dump v1.2 |
| |
| Build fingerprint: '' |
| |
| Total memory: 1000 |
| Allocation records: 2 |
| Backtrace size: 4 |
| |
| z 0 sz 400 num 1 bt a000 b000 c000 |
| z 0 sz 300 num 2 bt 100 200 |
| MAPS |
| MAP_DATA |
| END)"; |
| ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_full_dump_on_exit) { |
| pid_t pid; |
| if ((pid = fork()) == 0) { |
| std::shared_ptr<unwindstack::MapInfo> empty_map; |
| Init("backtrace=4 backtrace_full backtrace_dump_on_exit"); |
| BacktraceUnwindFake( |
| std::vector<unwindstack::FrameData>{{0, 0x100, 0x1100, 0, "fake1", 10, empty_map}, |
| {1, 0x200, 0x1200, 0, "fake2", 20, empty_map}}); |
| std::shared_ptr<unwindstack::MapInfo> map_info = |
| unwindstack::MapInfo::Create(0x10000, 0x20000, 0, PROT_READ | PROT_EXEC, "/data/fake.so"); |
| BacktraceUnwindFake( |
| std::vector<unwindstack::FrameData>{{0, 0xa000, 0x1a000, 0, "level1", 0, map_info}, |
| {1, 0xb000, 0x1b000, 0, "level2", 10, map_info}}); |
| BacktraceUnwindFake( |
| std::vector<unwindstack::FrameData>{{0, 0xa000, 0x1a000, 0, "func1", 0, empty_map}, |
| {1, 0xb000, 0x1b000, 0, "func2", 10, empty_map}, |
| {2, 0xc000, 0x1c000, 0, "", 30, empty_map}}); |
| |
| std::vector<void*> pointers; |
| pointers.push_back(debug_malloc(300)); |
| pointers.push_back(debug_malloc(400)); |
| pointers.push_back(debug_malloc(500)); |
| |
| // Call the exit function manually. |
| debug_finalize(); |
| _exit(0); |
| } |
| ASSERT_NE(-1, pid); |
| ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0))); |
| |
| // Read all of the contents. |
| std::string actual; |
| std::string name = android::base::StringPrintf("%s.%d.exit.txt", BACKTRACE_DUMP_PREFIX, pid); |
| ASSERT_TRUE(android::base::ReadFileToString(name, &actual)); |
| ASSERT_EQ(0, unlink(name.c_str())); |
| |
| std::string sanitized(SanitizeHeapData(actual)); |
| |
| std::string expected = |
| R"(Android Native Heap Dump v1.2 |
| |
| Build fingerprint: '' |
| |
| Total memory: 1200 |
| Allocation records: 3 |
| Backtrace size: 4 |
| |
| z 0 sz 500 num 1 bt 1a000 1b000 1c000 |
| bt_info {"" a000 "func1" 0} {"" b000 "func2" a} {"" c000 "" 0} |
| z 0 sz 400 num 1 bt 1a000 1b000 |
| bt_info {"/data/fake.so" a000 "level1" 0} {"/data/fake.so" b000 "level2" a} |
| z 0 sz 300 num 1 bt 1100 1200 |
| bt_info {"" 100 "fake1" a} {"" 200 "fake2" 14} |
| MAPS |
| MAP_DATA |
| END)"; |
| ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, realloc_usable_size) { |
| Init("front_guard"); |
| |
| // Verify that if the usable size > size of alloc, that realloc |
| // copies the bytes in the usable size not just the size. |
| // This assumes that an allocation of size 1 returns usable size > 1. |
| // If this isn't true, this test is not going to do anything. |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(1)); |
| ASSERT_TRUE(pointer != nullptr); |
| size_t usable_size = debug_malloc_usable_size(pointer); |
| memset(pointer, 0xaa, usable_size); |
| pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, usable_size + 10)); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_LE(usable_size + 10, debug_malloc_usable_size(pointer)); |
| for (size_t i = 0; i < usable_size; i++) { |
| ASSERT_EQ(0xaa, pointer[i]) << "Failed compare at byte " << i; |
| } |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_enable_on_signal) { |
| Init("backtrace_enable_on_signal=20"); |
| |
| size_t individual_size = GetInfoEntrySize(20); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300, 0x400}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x500, 0xa00, 0xb00}); |
| |
| // First allocation should not actually attempt to get the backtrace. |
| void* pointer = debug_malloc(10); |
| ASSERT_TRUE(pointer != nullptr); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info == nullptr); |
| ASSERT_EQ(0U, overall_size); |
| ASSERT_EQ(0U, info_size); |
| ASSERT_EQ(0U, total_memory); |
| ASSERT_EQ(0U, backtrace_size); |
| debug_free(pointer); |
| |
| debug_free_malloc_leak_info(info); |
| |
| // Send the signal to enable. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 19) == 0); |
| sleep(1); |
| |
| pointer = debug_malloc(100); |
| ASSERT_TRUE(pointer != nullptr); |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size, overall_size); |
| ASSERT_EQ(individual_size, info_size); |
| ASSERT_EQ(100U, total_memory); |
| ASSERT_EQ(20U, backtrace_size); |
| uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]); |
| ASSERT_EQ(0xbc000U, ips[0]); |
| ASSERT_EQ(0xecd00U, ips[1]); |
| ASSERT_EQ(0x12000U, ips[2]); |
| for (size_t i = 3; i < 20; i++) { |
| ASSERT_EQ(0U, ips[i]); |
| } |
| |
| debug_free(pointer); |
| |
| debug_free_malloc_leak_info(info); |
| |
| // Send the signal to disable. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 19) == 0); |
| sleep(1); |
| |
| pointer = debug_malloc(200); |
| ASSERT_TRUE(pointer != nullptr); |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info == nullptr); |
| ASSERT_EQ(0U, overall_size); |
| ASSERT_EQ(0U, info_size); |
| ASSERT_EQ(0U, total_memory); |
| ASSERT_EQ(0U, backtrace_size); |
| |
| debug_free(pointer); |
| |
| debug_free_malloc_leak_info(info); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_same_stack) { |
| Init("backtrace=4"); |
| |
| size_t individual_size = GetInfoEntrySize(4); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| |
| void* pointers[4]; |
| pointers[0] = debug_malloc(10); |
| ASSERT_TRUE(pointers[0] != nullptr); |
| pointers[1] = debug_malloc(10); |
| ASSERT_TRUE(pointers[1] != nullptr); |
| pointers[2] = debug_malloc(10); |
| ASSERT_TRUE(pointers[2] != nullptr); |
| pointers[3] = debug_malloc(100); |
| ASSERT_TRUE(pointers[3] != nullptr); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size * 2, overall_size); |
| ASSERT_EQ(individual_size, info_size); |
| EXPECT_EQ(130U, total_memory); |
| EXPECT_EQ(4U, backtrace_size); |
| EXPECT_EQ(100U, *reinterpret_cast<size_t*>(&info[0])); |
| EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)])); |
| uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0xecd00U, ips[1]); |
| EXPECT_EQ(0x12000U, ips[2]); |
| |
| EXPECT_EQ(10U, *reinterpret_cast<size_t*>(&info[individual_size])); |
| EXPECT_EQ(3U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + individual_size])); |
| ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + individual_size]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0xecd00U, ips[1]); |
| EXPECT_EQ(0x12000U, ips[2]); |
| |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointers[0]); |
| debug_free(pointers[1]); |
| debug_free(pointers[2]); |
| debug_free(pointers[3]); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_same_stack_zygote) { |
| Init("backtrace=4"); |
| |
| size_t individual_size = GetInfoEntrySize(4); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000}); |
| |
| zygote_child = true; |
| |
| void* pointers[4]; |
| pointers[0] = debug_malloc(100); |
| ASSERT_TRUE(pointers[0] != nullptr); |
| pointers[1] = debug_malloc(100); |
| ASSERT_TRUE(pointers[1] != nullptr); |
| pointers[2] = debug_malloc(100); |
| ASSERT_TRUE(pointers[2] != nullptr); |
| pointers[3] = debug_malloc(100); |
| ASSERT_TRUE(pointers[3] != nullptr); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size * 2, overall_size); |
| EXPECT_EQ(individual_size, info_size); |
| EXPECT_EQ(400U, total_memory); |
| EXPECT_EQ(4U, backtrace_size); |
| |
| EXPECT_EQ(0x80000064U, *reinterpret_cast<size_t*>(&info[0])); |
| EXPECT_EQ(3U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)])); |
| uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0xecd00U, ips[1]); |
| EXPECT_EQ(0x12000U, ips[2]); |
| |
| EXPECT_EQ(0x80000064U, *reinterpret_cast<size_t*>(&info[individual_size])); |
| EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + individual_size])); |
| ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + individual_size]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0U, ips[1]); |
| |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointers[0]); |
| debug_free(pointers[1]); |
| debug_free(pointers[2]); |
| debug_free(pointers[3]); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_same_stack_mix_zygote) { |
| Init("backtrace=4"); |
| |
| size_t individual_size = GetInfoEntrySize(4); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000}); |
| backtrace_fake_add(std::vector<uintptr_t> {0xbc000}); |
| |
| zygote_child = true; |
| void* pointers[4]; |
| pointers[0] = debug_malloc(40); |
| ASSERT_TRUE(pointers[0] != nullptr); |
| pointers[1] = debug_malloc(40); |
| ASSERT_TRUE(pointers[1] != nullptr); |
| |
| zygote_child = false; |
| pointers[2] = debug_malloc(40); |
| ASSERT_TRUE(pointers[2] != nullptr); |
| pointers[3] = debug_malloc(100); |
| ASSERT_TRUE(pointers[3] != nullptr); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size * 3, overall_size); |
| ASSERT_EQ(individual_size, info_size); |
| EXPECT_EQ(220U, total_memory); |
| EXPECT_EQ(4U, backtrace_size); |
| |
| EXPECT_EQ(100U, *reinterpret_cast<size_t*>(&info[0])); |
| EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)])); |
| uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0U, ips[1]); |
| |
| EXPECT_EQ(40U, *reinterpret_cast<size_t*>(&info[individual_size])); |
| EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + individual_size])); |
| ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + individual_size]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0xecd00U, ips[1]); |
| EXPECT_EQ(0x12000U, ips[2]); |
| |
| EXPECT_EQ(0x80000028U, *reinterpret_cast<size_t*>(&info[2 * individual_size])); |
| EXPECT_EQ(2U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + 2 * individual_size])); |
| ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + 2 * individual_size]); |
| EXPECT_EQ(0xbc000U, ips[0]); |
| EXPECT_EQ(0xecd00U, ips[1]); |
| EXPECT_EQ(0x12000U, ips[2]); |
| |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointers[0]); |
| debug_free(pointers[1]); |
| debug_free(pointers[2]); |
| debug_free(pointers[3]); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_frame_data_nullptr_same_size) { |
| Init("backtrace=4"); |
| |
| size_t individual_size = GetInfoEntrySize(4); |
| |
| void* pointers[4]; |
| pointers[0] = debug_malloc(100); |
| ASSERT_TRUE(pointers[0] != nullptr); |
| pointers[1] = debug_malloc(100); |
| ASSERT_TRUE(pointers[1] != nullptr); |
| pointers[2] = debug_malloc(100); |
| ASSERT_TRUE(pointers[2] != nullptr); |
| pointers[3] = debug_malloc(100); |
| ASSERT_TRUE(pointers[3] != nullptr); |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_TRUE(info != nullptr); |
| ASSERT_EQ(individual_size, overall_size); |
| EXPECT_EQ(individual_size, info_size); |
| EXPECT_EQ(400U, total_memory); |
| EXPECT_EQ(4U, backtrace_size); |
| |
| EXPECT_EQ(100U, *reinterpret_cast<size_t*>(&info[0])); |
| EXPECT_EQ(4U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)])); |
| uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]); |
| EXPECT_EQ(0U, ips[0]); |
| |
| debug_free_malloc_leak_info(info); |
| |
| debug_free(pointers[0]); |
| debug_free(pointers[1]); |
| debug_free(pointers[2]); |
| debug_free(pointers[3]); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, overflow) { |
| Init("guard fill_on_free"); |
| |
| void* pointer = debug_malloc(SIZE_MAX); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| pointer = debug_calloc(1, SIZE_MAX); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| pointer = debug_calloc(SIZE_MAX, 1); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| pointer = debug_calloc(SIZE_MAX/100, 100); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| pointer = debug_calloc(100, SIZE_MAX/100); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| const size_t size_t_bits = sizeof(size_t) * 8; |
| const size_t sqrt_size_t = 1ULL << (size_t_bits/2); |
| pointer = debug_calloc(sqrt_size_t + 1, sqrt_size_t); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| pointer = debug_realloc(nullptr, SIZE_MAX); |
| ASSERT_TRUE(pointer == nullptr); |
| ASSERT_EQ(ENOMEM, errno); |
| |
| pointer = debug_malloc(100); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0xd0, 100); |
| |
| void* realloc_pointer = debug_realloc(pointer, SIZE_MAX); |
| ASSERT_TRUE(realloc_pointer == nullptr); |
| // Verify the pointer was not freed. |
| for (size_t i = 0; i < 100; i++) { |
| ASSERT_EQ(0xd0, reinterpret_cast<uint8_t*>(pointer)[i]) << "Failed checking byte " << i; |
| } |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| static void VerifyZygoteSet(size_t memory_bytes) { |
| size_t expected_info_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t); |
| std::vector<uint8_t> expected_info(expected_info_size); |
| memset(expected_info.data(), 0, expected_info_size); |
| InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data()); |
| entry->size = memory_bytes | (1U << 31); |
| entry->num_allocations = 1; |
| entry->frames[0] = 0x1; |
| |
| uint8_t* info; |
| size_t overall_size; |
| size_t info_size; |
| size_t total_memory; |
| size_t backtrace_size; |
| |
| debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); |
| ASSERT_EQ(expected_info_size, overall_size); |
| ASSERT_EQ(expected_info_size, info_size); |
| ASSERT_EQ(memory_bytes, total_memory); |
| ASSERT_EQ(16U, backtrace_size); |
| ASSERT_TRUE(memcmp(info, expected_info.data(), expected_info_size) == 0) |
| << ShowDiffs(info, expected_info.data(), expected_info_size); |
| |
| debug_free_malloc_leak_info(info); |
| } |
| |
| TEST_F(MallocDebugTest, zygote_set) { |
| // Set all of the options. |
| Init("guard fill backtrace leak_track free_track=2"); |
| |
| zygote_child = true; |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1}); |
| |
| void* pointer = debug_malloc(100); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); |
| memset(pointer, 0, 100); |
| VerifyZygoteSet(100); |
| ASSERT_FALSE(HasFatalFailure()); |
| debug_free(pointer); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1}); |
| pointer = debug_calloc(10, 20); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(200U, debug_malloc_usable_size(pointer)); |
| VerifyZygoteSet(200); |
| ASSERT_FALSE(HasFatalFailure()); |
| debug_free(pointer); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1}); |
| pointer = debug_memalign(128, 300); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(300U, debug_malloc_usable_size(pointer)); |
| memset(pointer, 0, 300); |
| VerifyZygoteSet(300); |
| ASSERT_FALSE(HasFatalFailure()); |
| debug_free(pointer); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1}); |
| pointer = debug_malloc(500); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(500U, debug_malloc_usable_size(pointer)); |
| memset(pointer, 0, 500); |
| VerifyZygoteSet(500); |
| ASSERT_FALSE(HasFatalFailure()); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x1}); |
| pointer = debug_realloc(pointer, 300); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(300U, debug_malloc_usable_size(pointer)); |
| VerifyZygoteSet(300); |
| ASSERT_FALSE(HasFatalFailure()); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, max_size) { |
| Init("guard"); |
| |
| void* pointer = debug_malloc(1U << 31); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| pointer = debug_calloc(1, 1U << 31); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| pointer = debug_calloc(1U << 31, 1); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| pointer = debug_memalign(16, 1U << 31); |
| ASSERT_TRUE(pointer == nullptr); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, debug_mallinfo) { |
| SKIP_WITH_HWASAN; |
| Init("guard"); |
| |
| void* pointer = debug_malloc(150); |
| ASSERT_TRUE(pointer != nullptr); |
| |
| struct mallinfo mi = debug_mallinfo(); |
| EXPECT_NE(0U, mi.uordblks); |
| |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, debug_mallopt) { |
| Init("guard"); |
| |
| void* pointer = debug_malloc(150); |
| ASSERT_TRUE(pointer != nullptr); |
| |
| EXPECT_EQ(0, debug_mallopt(-1000, 1)); |
| |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, debug_posix_memalign) { |
| Init("guard"); |
| |
| void* pointer; |
| ASSERT_EQ(0, debug_posix_memalign(&pointer, 32, 300)); |
| ASSERT_TRUE(pointer != nullptr); |
| debug_free(pointer); |
| |
| ASSERT_EQ(EINVAL, debug_posix_memalign(&pointer, 11, 300)); |
| |
| ASSERT_EQ(ENOMEM, debug_posix_memalign(&pointer, 16, SIZE_MAX)); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) |
| TEST_F(MallocDebugTest, debug_pvalloc) { |
| Init("guard"); |
| |
| size_t pagesize = getpagesize(); |
| void* pointer = debug_pvalloc(1); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(pagesize, debug_malloc_usable_size(pointer)); |
| uintptr_t value = reinterpret_cast<uintptr_t>(pointer) & (pagesize - 1); |
| ASSERT_EQ(0U, value); |
| debug_free(pointer); |
| } |
| |
| TEST_F(MallocDebugTest, debug_valloc) { |
| Init("guard"); |
| |
| size_t pagesize = getpagesize(); |
| void* pointer = debug_valloc(100); |
| ASSERT_TRUE(pointer != nullptr); |
| ASSERT_EQ(100U, debug_malloc_usable_size(pointer)); |
| uintptr_t value = reinterpret_cast<uintptr_t>(pointer) & (pagesize - 1); |
| ASSERT_EQ(0U, value); |
| debug_free(pointer); |
| } |
| #endif |
| |
| void VerifyRecordAllocs(const std::string& record_filename) { |
| std::vector<std::string> expected; |
| |
| void* pointer = debug_malloc(10); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| pointer = debug_calloc(20, 1); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: calloc %p 20 1", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| pointer = debug_realloc(nullptr, 30); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: realloc %p 0x0 30", getpid(), pointer)); |
| void* old_pointer = pointer; |
| pointer = debug_realloc(pointer, 2048); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back( |
| android::base::StringPrintf("%d: realloc %p %p 2048", getpid(), pointer, old_pointer)); |
| debug_realloc(pointer, 0); |
| expected.push_back(android::base::StringPrintf("%d: realloc 0x0 %p 0", getpid(), pointer)); |
| |
| pointer = debug_memalign(16, 40); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: memalign %p 16 40", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| pointer = debug_aligned_alloc(32, 64); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: memalign %p 32 64", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| ASSERT_EQ(0, debug_posix_memalign(&pointer, 32, 50)); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: memalign %p 32 50", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| #if defined(HAVE_DEPRECATED_MALLOC_FUNCS) |
| pointer = debug_pvalloc(60); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: memalign %p 4096 4096", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| pointer = debug_valloc(70); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: memalign %p 4096 70", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| #endif |
| |
| // Dump all of the data accumulated so far. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0); |
| |
| // Read all of the contents. |
| std::string actual; |
| ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| VerifyRecords(expected, actual); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_no_header) { |
| InitRecordAllocs("record_allocs"); |
| |
| VerifyRecordAllocs(record_filename); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_with_header) { |
| InitRecordAllocs("record_allocs front_guard"); |
| |
| VerifyRecordAllocs(record_filename); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_max) { |
| InitRecordAllocs("record_allocs=5"); |
| |
| std::vector<std::string> expected; |
| |
| void* pointer = debug_malloc(10); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| pointer = debug_malloc(20); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 20", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| pointer = debug_malloc(1024); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 1024", getpid(), pointer)); |
| debug_free(pointer); |
| |
| // Dump all of the data accumulated so far. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0); |
| |
| // Read all of the contents. |
| std::string actual; |
| ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| VerifyRecords(expected, actual); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ( |
| "4 malloc_debug Maximum number of records added, all new operations will be dropped.\n", |
| getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_thread_done) { |
| InitRecordAllocs("record_allocs=5"); |
| |
| static pid_t tid = 0; |
| static void* pointer = nullptr; |
| std::thread thread([](){ |
| tid = gettid(); |
| pointer = debug_malloc(100); |
| write(0, pointer, 0); |
| debug_free(pointer); |
| }); |
| thread.join(); |
| |
| std::vector<std::string> expected; |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 100", tid, pointer)); |
| expected.push_back(android::base::StringPrintf("%d: free %p", tid, pointer)); |
| expected.push_back(android::base::StringPrintf("%d: thread_done 0x0", tid)); |
| |
| // Dump all of the data accumulated so far. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0); |
| |
| // Read all of the contents. |
| std::string actual; |
| ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| VerifyRecords(expected, actual); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_file_name_fail) { |
| InitRecordAllocs("record_allocs=5"); |
| |
| // Delete the records file and create a symbolic link there to |
| // make sure the create file will fail. |
| unlink(record_filename.c_str()); |
| |
| ASSERT_EQ(0, symlink("/data/local/tmp/does_not_exist", record_filename.c_str())); |
| |
| std::vector<std::string> expected; |
| |
| void* pointer = debug_malloc(10); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| // Dump all of the data accumulated so far. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0); |
| |
| // Read all of the contents. |
| std::string actual; |
| ASSERT_FALSE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| // Unlink the file so the next dump passes. |
| ASSERT_EQ(0, unlink(record_filename.c_str())); |
| |
| // Dump all of the data accumulated so far. |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 18) == 0); |
| |
| ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| VerifyRecords(expected, actual); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug Cannot create record alloc file %s: Too many symbolic links encountered\n", |
| record_filename.c_str()); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_no_entries_to_write) { |
| InitRecordAllocs("record_allocs=5"); |
| |
| kill(getpid(), SIGRTMAX - 18); |
| |
| std::string actual; |
| ASSERT_FALSE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("4 malloc_debug No alloc entries to write.\n", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_write_entries_does_not_allocate) { |
| InitRecordAllocs("record_allocs=5"); |
| |
| std::vector<std::string> expected; |
| |
| void* pointer = debug_malloc(10); |
| ASSERT_TRUE(pointer != nullptr); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 10", getpid(), pointer)); |
| debug_free(pointer); |
| expected.push_back(android::base::StringPrintf("%d: free %p", getpid(), pointer)); |
| |
| malloc_disable(); |
| kill(getpid(), SIGRTMAX - 18); |
| malloc_enable(); |
| |
| std::string actual; |
| ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual)); |
| |
| VerifyRecords(expected, actual); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, record_allocs_on_exit) { |
| InitRecordAllocs("record_allocs record_allocs_on_exit"); |
| |
| // The filename created on exit always appends the pid. |
| // Modify the variable so the file is deleted at the end of the test. |
| record_filename += '.' + std::to_string(getpid()); |
| |
| std::vector<std::string> expected; |
| void* ptr = debug_malloc(100); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 100", getpid(), ptr)); |
| ptr = debug_malloc(200); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 200", getpid(), ptr)); |
| ptr = debug_malloc(400); |
| expected.push_back(android::base::StringPrintf("%d: malloc %p 400", getpid(), ptr)); |
| |
| // Call the exit function manually. |
| debug_finalize(); |
| |
| // Read all of the contents. |
| std::string actual; |
| ASSERT_TRUE(android::base::ReadFileToString(record_filename, &actual)); |
| VerifyRecords(expected, actual); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, verify_pointers) { |
| Init("verify_pointers"); |
| |
| void* pointer = debug_malloc(10); |
| memset(pointer, 0, 10); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| |
| debug_free(pointer); |
| ASSERT_EQ(0U, debug_malloc_usable_size(pointer)); |
| ASSERT_EQ(nullptr, debug_realloc(pointer, 1000)); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string free_pointer_str( |
| android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p UNKNOWN POINTER (free)\n", |
| pointer)); |
| std::string usable_pointer_str( |
| android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p UNKNOWN POINTER (malloc_usable_size)\n", |
| pointer)); |
| std::string realloc_pointer_str( |
| android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p UNKNOWN POINTER (realloc)\n", |
| pointer)); |
| std::string backtrace_str("6 malloc_debug Backtrace at time of failure:\n"); |
| backtrace_str += "6 malloc_debug Backtrace failed to get any frames.\n"; |
| |
| std::string expected_log(DIVIDER + free_pointer_str + backtrace_str + DIVIDER); |
| expected_log += DIVIDER + usable_pointer_str + backtrace_str + DIVIDER; |
| expected_log += DIVIDER + realloc_pointer_str + backtrace_str + DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| |
| resetLogs(); |
| |
| backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x300, 0x400}); |
| backtrace_fake_add(std::vector<uintptr_t> {0x500, 0x600}); |
| debug_free(pointer); |
| ASSERT_EQ(0U, debug_malloc_usable_size(pointer)); |
| ASSERT_EQ(nullptr, debug_realloc(pointer, 1000)); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| expected_log = DIVIDER + free_pointer_str; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x100\n"; |
| expected_log += "6 malloc_debug #01 pc 0x200\n"; |
| expected_log += DIVIDER; |
| expected_log += DIVIDER + usable_pointer_str; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x300\n"; |
| expected_log += "6 malloc_debug #01 pc 0x400\n"; |
| expected_log += DIVIDER; |
| expected_log += DIVIDER + realloc_pointer_str; |
| expected_log += "6 malloc_debug Backtrace at time of failure:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x500\n"; |
| expected_log += "6 malloc_debug #01 pc 0x600\n"; |
| expected_log += DIVIDER; |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, abort_on_error_log_error) { |
| Init("abort_on_error verify_pointers"); |
| |
| void* pointer = debug_malloc(10); |
| memset(pointer, 0, 10); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| ASSERT_STREQ("", getFakeLogPrint().c_str()); |
| |
| EXPECT_DEATH(debug_free(pointer), ""); |
| } |
| |
| TEST_F(MallocDebugTest, abort_on_error_guard_corrupted) { |
| Init("abort_on_error front_guard=32"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| pointer[-16] = 0x00; |
| EXPECT_DEATH(debug_free(pointer), ""); |
| pointer[-16] = 0xaa; |
| debug_free(pointer); |
| } |
| |
| TEST_F(MallocDebugTest, abort_on_error_use_after_free) { |
| Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 100); |
| debug_free(pointer); |
| |
| pointer[56] = 0x91; |
| |
| EXPECT_DEATH(debug_finalize(), ""); |
| |
| pointer[56] = 0xef; |
| } |
| |
| TEST_F(MallocDebugTest, abort_on_error_header_tag_corrupted) { |
| Init("abort_on_error free_track=100 free_track_backtrace_num_frames=0 rear_guard"); |
| |
| uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100)); |
| ASSERT_TRUE(pointer != nullptr); |
| memset(pointer, 0, 100); |
| debug_free(pointer); |
| |
| uint8_t tag_value = pointer[-get_tag_offset()]; |
| pointer[-get_tag_offset()] = 0x00; |
| |
| EXPECT_DEATH(debug_finalize(), ""); |
| |
| pointer[-get_tag_offset()] = tag_value; |
| } |
| |
| TEST_F(MallocDebugTest, malloc_info_no_pointer_tracking) { |
| SKIP_WITH_HWASAN; |
| Init("fill"); |
| |
| 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(contents.c_str())); |
| 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); |
| |
| 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)); |
| |
| SCOPED_TRACE(testing::Message() << "Output:\n" << contents); |
| |
| tinyxml2::XMLDocument doc; |
| ASSERT_EQ(tinyxml2::XML_SUCCESS, doc.Parse(contents.c_str())); |
| 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); |
| } |
| |
| 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()); |
| } |
| |
| extern "C" bool LogUnreachableMemory(bool, size_t) { |
| static bool return_value = false; |
| return_value = !return_value; |
| return return_value; |
| } |
| |
| TEST_F(MallocDebugTest, check_unreachable_on_signal) { |
| Init("check_unreachable_on_signal"); |
| |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 16) == 0); |
| sleep(1); |
| |
| // The first unreachable check will pass. |
| void* pointer = debug_malloc(110); |
| ASSERT_TRUE(pointer != nullptr); |
| |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 16) == 0); |
| sleep(1); |
| |
| // The second unreachable check will fail. |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = "4 malloc_debug Starting to check for unreachable memory.\n"; |
| ASSERT_STREQ( |
| "4 malloc_debug Starting to check for unreachable memory.\n" |
| "4 malloc_debug Starting to check for unreachable memory.\n" |
| "6 malloc_debug Unreachable check failed, run setenforce 0 and try again.\n", |
| getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, log_allocator_stats_on_signal) { |
| Init("log_allocator_stats_on_signal"); |
| |
| ASSERT_TRUE(kill(getpid(), SIGRTMAX - 15) == 0); |
| sleep(1); |
| |
| // The first unreachable check will pass. |
| void* pointer = debug_malloc(110); |
| ASSERT_TRUE(pointer != nullptr); |
| debug_free(pointer); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| if (!running_with_hwasan()) { |
| // Do an exact match because the mallopt should not fail in normal operation. |
| ASSERT_STREQ("4 malloc_debug Logging allocator stats...\n", getFakeLogPrint().c_str()); |
| } else { |
| // mallopt fails with hwasan, so just verify that the message is present. |
| ASSERT_MATCH(getFakeLogPrint(), "4 malloc_debug Logging allocator stats...\\n"); |
| } |
| } |
| |
| TEST_F(MallocDebugTest, log_allocator_stats_on_exit) { |
| Init("log_allocator_stats_on_exit"); |
| |
| void* pointer = debug_malloc(110); |
| ASSERT_TRUE(pointer != nullptr); |
| debug_free(pointer); |
| |
| debug_finalize(); |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| if (!running_with_hwasan()) { |
| // Do an exact match because the mallopt should not fail in normal operation. |
| ASSERT_STREQ("4 malloc_debug Logging allocator stats...\n", getFakeLogPrint().c_str()); |
| } else { |
| // mallopt fails with hwasan, so just verify that the message is present. |
| ASSERT_MATCH(getFakeLogPrint(), "4 malloc_debug Logging allocator stats...\\n"); |
| } |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_size) { |
| Init("leak_track backtrace backtrace_size=120"); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0x1000, 0x2000, 0x3000}); |
| |
| void* pointer1 = debug_malloc(119); |
| ASSERT_TRUE(pointer1 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xa000, 0xb000, 0xc000, 0xd000}); |
| |
| void* pointer2 = debug_malloc(120); |
| ASSERT_TRUE(pointer2 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000}); |
| |
| void* pointer3 = debug_malloc(121); |
| ASSERT_TRUE(pointer3 != nullptr); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 121 at %p (leak 1 of 3)\n", pointer3); |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 120 at %p (leak 2 of 3)\n", pointer2); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x1000\n"; |
| expected_log += "6 malloc_debug #01 pc 0x2000\n"; |
| expected_log += "6 malloc_debug #02 pc 0x3000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 119 at %p (leak 3 of 3)\n", pointer1); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_min_size) { |
| Init("leak_track backtrace backtrace_min_size=1000"); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0x1000, 0x2000, 0x3000}); |
| |
| void* pointer1 = debug_malloc(500); |
| ASSERT_TRUE(pointer1 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xa000, 0xb000, 0xc000, 0xd000}); |
| |
| void* pointer2 = debug_malloc(1000); |
| ASSERT_TRUE(pointer2 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000}); |
| |
| void* pointer3 = debug_malloc(1001); |
| ASSERT_TRUE(pointer3 != nullptr); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1001 at %p (leak 1 of 3)\n", |
| pointer3); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xa000\n"; |
| expected_log += "6 malloc_debug #01 pc 0xb000\n"; |
| expected_log += "6 malloc_debug #02 pc 0xc000\n"; |
| expected_log += "6 malloc_debug #03 pc 0xd000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1000 at %p (leak 2 of 3)\n", |
| pointer2); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x1000\n"; |
| expected_log += "6 malloc_debug #01 pc 0x2000\n"; |
| expected_log += "6 malloc_debug #02 pc 0x3000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 500 at %p (leak 3 of 3)\n", pointer1); |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_max_size) { |
| Init("leak_track backtrace backtrace_max_size=1000"); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0x1000, 0x2000, 0x3000}); |
| |
| void* pointer1 = debug_malloc(1000); |
| ASSERT_TRUE(pointer1 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xa000, 0xb000, 0xc000, 0xd000}); |
| |
| void* pointer2 = debug_malloc(1001); |
| ASSERT_TRUE(pointer2 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000}); |
| |
| void* pointer3 = debug_malloc(5000); |
| ASSERT_TRUE(pointer3 != nullptr); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 5000 at %p (leak 1 of 3)\n", |
| pointer3); |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1001 at %p (leak 2 of 3)\n", |
| pointer2); |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1000 at %p (leak 3 of 3)\n", |
| pointer1); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x1000\n"; |
| expected_log += "6 malloc_debug #01 pc 0x2000\n"; |
| expected_log += "6 malloc_debug #02 pc 0x3000\n"; |
| |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |
| |
| TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_min_max_size) { |
| Init("leak_track backtrace backtrace_min_size=50 backtrace_max_size=1000"); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0x1000, 0x2000, 0x3000}); |
| |
| void* pointer1 = debug_malloc(49); |
| ASSERT_TRUE(pointer1 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xa000, 0xb000, 0xc000, 0xd000}); |
| |
| void* pointer2 = debug_malloc(50); |
| ASSERT_TRUE(pointer2 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000}); |
| |
| void* pointer3 = debug_malloc(1000); |
| ASSERT_TRUE(pointer3 != nullptr); |
| |
| backtrace_fake_add(std::vector<uintptr_t>{0x1a000, 0x1b000, 0x1c000, 0x1d000, 0x1e000}); |
| |
| void* pointer4 = debug_malloc(1001); |
| ASSERT_TRUE(pointer4 != nullptr); |
| |
| debug_finalize(); |
| initialized = false; |
| |
| ASSERT_STREQ("", getFakeLogBuf().c_str()); |
| std::string expected_log = android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1001 at %p (leak 1 of 4)\n", |
| pointer4); |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 1000 at %p (leak 2 of 4)\n", |
| pointer3); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0xa000\n"; |
| expected_log += "6 malloc_debug #01 pc 0xb000\n"; |
| expected_log += "6 malloc_debug #02 pc 0xc000\n"; |
| expected_log += "6 malloc_debug #03 pc 0xd000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 50 at %p (leak 3 of 4)\n", pointer2); |
| expected_log += "6 malloc_debug Backtrace at time of allocation:\n"; |
| expected_log += "6 malloc_debug #00 pc 0x1000\n"; |
| expected_log += "6 malloc_debug #01 pc 0x2000\n"; |
| expected_log += "6 malloc_debug #02 pc 0x3000\n"; |
| |
| expected_log += android::base::StringPrintf( |
| "6 malloc_debug +++ malloc_testing leaked block of size 49 at %p (leak 4 of 4)\n", pointer1); |
| |
| ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str()); |
| } |