Add support for using the new unwinder.

This adds a new option backtrace_full, when it is set, then it will use
libunwindstack.

Modify the dump to file data to dump the extra information from libunwindstack.
Along with the new dump file format, change the version to v1.1.
Updated document for new format of file data.

Add unit tests for the new functionality.

Bug: 74361929

Test: Ran unit tests.
Change-Id: I40fff795f5346bba7b9d7fde2e04f269ff4eb7f1
This commit is contained in:
Christopher Ferris 2018-04-05 11:12:38 -07:00
parent 4c5c45346f
commit 93bdd6ae3a
16 changed files with 507 additions and 110 deletions

View file

@ -14,6 +14,7 @@ cc_library_static {
stl: "libc++_static",
whole_static_libs: [
"libbase",
"libasync_safe",
"libdemangle",
],
@ -49,6 +50,7 @@ cc_library {
"malloc_debug.cpp",
"PointerData.cpp",
"RecordData.cpp",
"UnwindBacktrace.cpp",
],
stl: "libc++_static",
@ -69,9 +71,14 @@ cc_library {
static_libs: [
"libasync_safe",
"libbase",
"libdemangle",
"libc_malloc_debug_backtrace",
],
shared_libs: [
"libunwindstack",
],
multilib: {
lib32: {
version_script: "exported32.map",
@ -112,15 +119,21 @@ cc_test {
"tests/malloc_debug_unit_tests.cpp",
],
whole_static_libs: ["libc_malloc_debug"],
local_include_dirs: ["tests"],
include_dirs: [
"bionic/libc",
"bionic/libc/async_safe/include",
],
shared_libs: ["libbase"],
static_libs: [
"libc_malloc_debug",
"libdemangle",
],
shared_libs: [
"libbase",
"libunwindstack",
],
cflags: [
"-Wall",

View file

@ -93,6 +93,9 @@ const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = {
{
"backtrace_dump_prefix", {0, &Config::SetBacktraceDumpPrefix},
},
{
"backtrace_full", {BACKTRACE_FULL, &Config::VerifyValueEmpty},
},
{
"fill", {FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
@ -363,6 +366,11 @@ void Config::LogUsage() const {
error_log(" backtrace_dump_prefix.<PID>.final.txt.");
error_log(" The default is false.");
error_log("");
error_log(" backtrace_full");
error_log(" Any time a backtrace is acquired, use an unwinder that can");
error_log(" display Java stack frames. This unwinder can run slower than");
error_log(" normal unwinder.");
error_log("");
error_log(" fill_on_alloc[=XX]");
error_log(" On first allocation, fill with the value 0x%02x.", DEFAULT_FILL_ALLOC_VALUE);
error_log(" If XX is set it will only fill up to XX bytes of the");

View file

@ -43,6 +43,7 @@ constexpr uint64_t FREE_TRACK = 0x40;
constexpr uint64_t TRACK_ALLOCS = 0x80;
constexpr uint64_t LEAK_TRACK = 0x100;
constexpr uint64_t RECORD_ALLOCS = 0x200;
constexpr uint64_t BACKTRACE_FULL = 0x400;
// In order to guarantee posix compliance, set the minimum alignment
// to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.

View file

@ -62,10 +62,7 @@ void GuardData::LogFailure(const Header* header, const void* pointer, const void
}
error_log("Backtrace at time of failure:");
std::vector<uintptr_t> frames(64);
size_t frame_num = backtrace_get(frames.data(), frames.size());
frames.resize(frame_num);
backtrace_log(frames.data(), frames.size());
BacktraceAndLog();
error_log(LOG_DIVIDER);
}

View file

@ -43,6 +43,7 @@
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <demangle.h>
#include <private/bionic_macros.h>
#include "Config.h"
@ -51,6 +52,7 @@
#include "backtrace.h"
#include "debug_log.h"
#include "malloc_debug.h"
#include "UnwindBacktrace.h"
std::atomic_uint8_t PointerData::backtrace_enabled_;
std::atomic_bool PointerData::backtrace_dump_;
@ -63,6 +65,7 @@ std::mutex PointerData::frame_mutex_;
std::unordered_map<FrameKeyType, size_t> PointerData::key_to_index_ GUARDED_BY(
PointerData::frame_mutex_);
std::unordered_map<size_t, FrameInfoType> PointerData::frames_ GUARDED_BY(PointerData::frame_mutex_);
std::unordered_map<size_t, std::vector<unwindstack::LocalFrameData>> PointerData::backtraces_info_ GUARDED_BY(PointerData::frame_mutex_);
constexpr size_t kBacktraceEmptyIndex = 1;
size_t PointerData::cur_hash_index_ GUARDED_BY(PointerData::frame_mutex_);
@ -127,10 +130,18 @@ bool PointerData::Initialize(const Config& config) NO_THREAD_SAFETY_ANALYSIS {
}
size_t PointerData::AddBacktrace(size_t num_frames) {
std::vector<uintptr_t> frames(num_frames);
num_frames = backtrace_get(frames.data(), frames.size());
if (num_frames == 0) {
return kBacktraceEmptyIndex;
std::vector<uintptr_t> frames;
std::vector<unwindstack::LocalFrameData> frames_info;
if (g_debug->config().options() & BACKTRACE_FULL) {
if (!Unwind(&frames, &frames_info, num_frames)) {
return kBacktraceEmptyIndex;
}
} else {
frames.resize(num_frames);
num_frames = backtrace_get(frames.data(), frames.size());
if (num_frames == 0) {
return kBacktraceEmptyIndex;
}
}
FrameKeyType key{.num_frames = num_frames, .frames = frames.data()};
@ -144,6 +155,9 @@ size_t PointerData::AddBacktrace(size_t num_frames) {
key_to_index_.emplace(key, hash_index);
frames_.emplace(hash_index, FrameInfoType{.references = 1, .frames = std::move(frames)});
if (g_debug->config().options() & BACKTRACE_FULL) {
backtraces_info_.emplace(hash_index, std::move(frames_info));
}
} else {
hash_index = entry->second;
FrameInfoType* frame_info = &frames_[hash_index];
@ -168,6 +182,9 @@ void PointerData::RemoveBacktrace(size_t hash_index) {
FrameKeyType key{.num_frames = frame_info->frames.size(), .frames = frame_info->frames.data()};
key_to_index_.erase(key);
frames_.erase(hash_index);
if (g_debug->config().options() & BACKTRACE_FULL) {
backtraces_info_.erase(hash_index);
}
}
}
@ -230,6 +247,25 @@ size_t PointerData::GetFrames(const void* ptr, uintptr_t* frames, size_t max_fra
return max_frames;
}
void PointerData::LogBacktrace(size_t hash_index) {
std::lock_guard<std::mutex> frame_guard(frame_mutex_);
if (g_debug->config().options() & BACKTRACE_FULL) {
auto backtrace_info_entry = backtraces_info_.find(hash_index);
if (backtrace_info_entry != backtraces_info_.end()) {
UnwindLog(backtrace_info_entry->second);
return;
}
} else {
auto frame_entry = frames_.find(hash_index);
if (frame_entry != frames_.end()) {
FrameInfoType* frame_info = &frame_entry->second;
backtrace_log(frame_info->frames.data(), frame_info->frames.size());
return;
}
}
error_log(" hash_index %zu does not have matching frame data.", hash_index);
}
void PointerData::LogFreeError(const FreePointerInfoType& info, size_t usable_size) {
error_log(LOG_DIVIDER);
uint8_t* memory = reinterpret_cast<uint8_t*>(info.pointer);
@ -242,13 +278,8 @@ void PointerData::LogFreeError(const FreePointerInfoType& info, size_t usable_si
}
if (info.hash_index > kBacktraceEmptyIndex) {
std::lock_guard<std::mutex> frame_guard(frame_mutex_);
auto frame_entry = frames_.find(info.hash_index);
if (frame_entry != frames_.end()) {
FrameInfoType* frame_info = &frame_entry->second;
error_log("Backtrace at time of free:");
backtrace_log(frame_info->frames.data(), frame_info->frames.size());
}
error_log("Backtrace at time of free:");
LogBacktrace(info.hash_index);
}
error_log(LOG_DIVIDER);
@ -328,15 +359,8 @@ void PointerData::LogFreeBacktrace(const void* ptr) {
return;
}
std::lock_guard<std::mutex> frame_guard(frame_mutex_);
auto frame_entry = frames_.find(hash_index);
if (frame_entry == frames_.end()) {
error_log("Freed pointer hash_index %zu does not have matching frame data.", hash_index);
return;
}
FrameInfoType* frame_info = &frame_entry->second;
error_log("Backtrace of original free:");
backtrace_log(frame_info->frames.data(), frame_info->frames.size());
LogBacktrace(hash_index);
}
void PointerData::VerifyAllFreed() {
@ -350,18 +374,28 @@ void PointerData::GetList(std::vector<ListInfoType>* list, bool only_with_backtr
REQUIRES(pointer_mutex_, frame_mutex_) {
for (const auto& entry : pointers_) {
FrameInfoType* frame_info = nullptr;
std::vector<unwindstack::LocalFrameData>* backtrace_info = nullptr;
size_t hash_index = entry.second.hash_index;
if (hash_index > kBacktraceEmptyIndex) {
frame_info = &frames_[hash_index];
if (frame_info->references == 0) {
auto frame_entry = frames_.find(hash_index);
if (frame_entry == frames_.end()) {
// Somehow wound up with a pointer with a valid hash_index, but
// no frame data. This should not be possible since adding a pointer
// occurs after the hash_index and frame data have been added.
// When removing a pointer, the pointer is deleted before the frame
// data.
frames_.erase(hash_index);
error_log("Pointer 0x%" PRIxPTR " hash_index %zu does not exist.", entry.first, hash_index);
frame_info = nullptr;
} else {
frame_info = &frame_entry->second;
}
if (g_debug->config().options() & BACKTRACE_FULL) {
auto backtrace_entry = backtraces_info_.find(hash_index);
if (backtrace_entry == backtraces_info_.end()) {
error_log("Pointer 0x%" PRIxPTR " hash_index %zu does not exist.", entry.first, hash_index);
} else {
backtrace_info = &backtrace_entry->second;
}
}
}
if (hash_index == 0 && only_with_backtrace) {
@ -369,7 +403,7 @@ void PointerData::GetList(std::vector<ListInfoType>* list, bool only_with_backtr
}
list->emplace_back(ListInfoType{entry.first, 1, entry.second.RealSize(),
entry.second.ZygoteChildAlloc(), frame_info});
entry.second.ZygoteChildAlloc(), frame_info, backtrace_info});
}
// Sort by the size of the allocation.
@ -440,7 +474,10 @@ void PointerData::LogLeaks() {
for (const auto& list_info : list) {
error_log("+++ %s leaked block of size %zu at 0x%" PRIxPTR " (leak %zu of %zu)", getprogname(),
list_info.size, list_info.pointer, ++track_count, list.size());
if (list_info.frame_info != nullptr) {
if (list_info.backtrace_info != nullptr) {
error_log("Backtrace at time of allocation:");
UnwindLog(*list_info.backtrace_info);
} else if (list_info.frame_info != nullptr) {
error_log("Backtrace at time of allocation:");
backtrace_log(list_info.frame_info->frames.data(), list_info.frame_info->frames.size());
}
@ -520,14 +557,28 @@ void PointerData::DumpLiveToFile(FILE* fp) {
if (frame_info->frames[i] == 0) {
break;
}
#if defined(__LP64__)
fprintf(fp, " %016" PRIxPTR, frame_info->frames[i]);
#else
fprintf(fp, " %08" PRIxPTR, frame_info->frames[i]);
#endif
fprintf(fp, " %" PRIxPTR, frame_info->frames[i]);
}
}
fprintf(fp, "\n");
if (info.backtrace_info != nullptr) {
fprintf(fp, " bt_info");
for (const auto& frame : *info.backtrace_info) {
fprintf(fp, " {");
if (frame.map_info != nullptr && !frame.map_info->name.empty()) {
fprintf(fp, "\"%s\"", frame.map_info->name.c_str());
} else {
fprintf(fp, "\"\"");
}
fprintf(fp, " %" PRIx64, frame.rel_pc);
if (frame.function_name.empty()) {
fprintf(fp, " \"\" 0}");
} else {
fprintf(fp, " \"%s\" %" PRIx64 "}", demangle(frame.function_name.c_str()).c_str(), frame.function_offset);
}
}
fprintf(fp, "\n");
}
}
}

View file

@ -39,8 +39,10 @@
#include <vector>
#include <private/bionic_macros.h>
#include <unwindstack/LocalUnwinder.h>
#include "OptionData.h"
#include "UnwindBacktrace.h"
extern int* g_malloc_zygote_child;
@ -105,6 +107,7 @@ struct ListInfoType {
size_t size;
bool zygote_child_alloc;
FrameInfoType* frame_info;
std::vector<unwindstack::LocalFrameData>* backtrace_info;
};
class PointerData : public OptionData {
@ -160,6 +163,7 @@ class PointerData : public OptionData {
private:
static std::string GetHashString(uintptr_t* frames, size_t num_frames);
static void LogBacktrace(size_t hash_index);
size_t alloc_offset_ = 0;
std::vector<uint8_t> cmp_mem_;
@ -174,6 +178,7 @@ class PointerData : public OptionData {
static std::mutex frame_mutex_;
static std::unordered_map<FrameKeyType, size_t> key_to_index_;
static std::unordered_map<size_t, FrameInfoType> frames_;
static std::unordered_map<size_t, std::vector<unwindstack::LocalFrameData>> backtraces_info_;
static size_t cur_hash_index_;
static std::mutex free_pointer_mutex_;

View file

@ -150,8 +150,8 @@ The file location can be changed by setting the backtrace\_dump\_prefix
option.
### backtrace\_dump\_prefix
As of P, when the backtrace options has been enabled, this sets the prefix
used for dumping files when the signal SIGRTMAX - 17 is received or when
As of P, when one of the backtrace options has been enabled, this sets the
prefix used for dumping files when the signal SIGRTMAX - 17 is received or when
the program exits and backtrace\_dump\_on\_exit is set.
The default is /data/local/tmp/backtrace\_heap.
@ -160,6 +160,11 @@ When this value is changed from the default, then the filename chosen
on the signal will be backtrace\_dump\_prefix.**PID**.txt. The filename chosen
when the program exits will be backtrace\_dump\_prefix.**PID**.exit.txt.
### backtrace\_full
As of P, any time that a backtrace is gathered, a different algorithm is used
that is extra thorough and can unwind through Java frames. This will run
slower than the normal backtracing function.
### fill\_on\_alloc[=MAX\_FILLED\_BYTES]
Any allocation routine, other than calloc, will result in the allocation being
filled with the value 0xeb. When doing a realloc to a larger size, the bytes
@ -490,6 +495,45 @@ The final section is the map data for the process:
The map data is simply the output of /proc/PID/maps. This data can be used to
decode the frames in the backtraces.
As of Android P, there is a new version of this file. The new header is:
Android Native Heap Dump v1.1
The new version no longer 0 pads the backtrace addresses. In v1.0:
z 0 sz 400 num 1 bt 0000a230 0000b500
While v1.1:
z 0 sz 400 num 1 bt a230 b500
In addition, when the new option backtrace\_full is used, another line will
be added to every backtrace line. The line will be:
bt_info {"MAP_NAME" RELATIVE_TO_MAP_PC "FUNCTION_NAME" FUNCTION_OFFSET} ...
For each backtrace pc, there will be one element in braces.
MAP\_NAME is the name of the map in which the backtrace pc exists. If there is
no valid map name, this will be empty.
RELATIVE\_TO\_MAP\_PC is the hexadecimal value of the relative pc to the map.
FUNCTION\_NAME the name of the function for this pc. If there is no valid
function name, then it will be empty.
FUNCTION\_OFFSET the hexadecimal offset from the beginning of the function. If
the FUNCTION\_NAME is empty, then this value will always be zero.
An example of this new format:
z 0 sz 400 num 1 bt a2a0 b510
bt_info {"/system/libc.so" 2a0 "abort" 24} {"/system/libutils.so" 510 "" 0}
In this example, the first backtrace frame has a pc of 0xa2a0 and is in the
map named /system/libc.so which starts at 0xa000. The relative pc is 0x2a0,
and it is in the function abort + 0x24.
The second backtrace frame has a pc of 0xb510 and is in the map named
/system/libutils.so which starts at 0xb000. The relative pc is 0x510 and
it is in an unknown function.
There is a tool to visualize this data, development/scripts/native\_heapdump\_viewer.py.
Examples

View file

@ -0,0 +1,111 @@
/*
* Copyright (C) 2018 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <android-base/stringprintf.h>
#include <demangle.h>
#include <unwindstack/LocalUnwinder.h>
#include <unwindstack/MapInfo.h>
#include "UnwindBacktrace.h"
#include "debug_log.h"
#if defined(__LP64__)
#define PAD_PTR "016" PRIx64
#else
#define PAD_PTR "08" PRIx64
#endif
static pthread_once_t g_setup_once = PTHREAD_ONCE_INIT;
static unwindstack::LocalUnwinder* g_unwinder;
static void Setup() {
#if defined(__LP64__)
std::vector<std::string> skip_libraries{"/system/lib64/libunwindstack.so", "/system/lib64/libc_malloc_debug.so"};
#else
std::vector<std::string> skip_libraries{"/system/lib/libunwindstack.so", "/system/lib/libc_malloc_debug.so"};
#endif
g_unwinder = new unwindstack::LocalUnwinder(skip_libraries);
g_unwinder->Init();
}
bool Unwind(std::vector<uintptr_t>* frames, std::vector<unwindstack::LocalFrameData>* frame_info, size_t max_frames) {
pthread_once(&g_setup_once, Setup);
if (g_unwinder == nullptr) {
return false;
}
if (!g_unwinder->Unwind(frame_info, max_frames)) {
frames->clear();
frame_info->clear();
return false;
}
for (const auto& frame : *frame_info) {
frames->push_back(frame.pc);
}
return true;
}
void UnwindLog(const std::vector<unwindstack::LocalFrameData>& frame_info) {
for (size_t i = 0; i < frame_info.size(); i++) {
const unwindstack::LocalFrameData* info = &frame_info[i];
unwindstack::MapInfo* map_info = info->map_info;
std::string line = android::base::StringPrintf(" #%0zd pc %" PAD_PTR " ", i, info->rel_pc);
if (map_info->offset != 0) {
line += android::base::StringPrintf("(offset 0x%" PRIx64 ") ", map_info->offset);
}
if (map_info->name.empty()) {
line += android::base::StringPrintf("<anonymous:%" PRIx64 ">", map_info->start);
} else {
line += map_info->name;
}
if (!info->function_name.empty()) {
line += " (" + demangle(info->function_name.c_str());
if (info->function_offset != 0) {
line += "+" + std::to_string(info->function_offset);
}
line += ")";
}
error_log_string(line.c_str());
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
#include <unwindstack/LocalUnwinder.h>
#include <unwindstack/MapInfo.h>
bool Unwind(std::vector<uintptr_t>* frames, std::vector<unwindstack::LocalFrameData>* info, size_t max_frames);
void UnwindLog(const std::vector<unwindstack::LocalFrameData>& frame_info);

View file

@ -47,6 +47,7 @@
#include "debug_disable.h"
#include "debug_log.h"
#include "malloc_debug.h"
#include "UnwindBacktrace.h"
// ------------------------------------------------------------------------
// Global Data
@ -118,6 +119,26 @@ static void InitAtfork() {
});
}
void BacktraceAndLog() {
if (g_debug->config().options() & BACKTRACE_FULL) {
std::vector<uintptr_t> frames;
std::vector<unwindstack::LocalFrameData> frames_info;
if (!Unwind(&frames, &frames_info, 256)) {
error_log(" Backtrace failed to get any frames.");
} else {
UnwindLog(frames_info);
}
} else {
std::vector<uintptr_t> frames(256);
size_t num_frames = backtrace_get(frames.data(), frames.size());
if (num_frames == 0) {
error_log(" Backtrace failed to get any frames.");
} else {
backtrace_log(frames.data(), num_frames);
}
}
}
static void LogError(const void* pointer, const char* error_str) {
error_log(LOG_DIVIDER);
error_log("+++ ALLOCATION %p %s", pointer, error_str);
@ -128,14 +149,8 @@ static void LogError(const void* pointer, const char* error_str) {
PointerData::LogFreeBacktrace(pointer);
}
std::vector<uintptr_t> frames(128);
size_t num_frames = backtrace_get(frames.data(), frames.size());
if (num_frames == 0) {
error_log("Backtrace failed to get any frames.");
} else {
error_log("Backtrace at time of failure:");
backtrace_log(frames.data(), num_frames);
}
error_log("Backtrace at time of failure:");
BacktraceAndLog();
error_log(LOG_DIVIDER);
}
@ -819,7 +834,7 @@ bool debug_dump_heap(const char* file_name) {
return false;
}
fprintf(fp, "Android Native Heap Dump v1.0\n\n");
fprintf(fp, "Android Native Heap Dump v1.1\n\n");
PointerData::DumpLiveToFile(fp);

View file

@ -62,3 +62,5 @@ constexpr char LOG_DIVIDER[] = "*** *** *** *** *** *** *** *** *** *** *** ***
constexpr size_t FREE_TRACK_MEM_BUFFER_SIZE = 4096;
extern const MallocDispatch* g_dispatch;
void BacktraceAndLog();

View file

@ -20,6 +20,8 @@
#include <vector>
#include <utility>
#include <unwindstack/LocalUnwinder.h>
#include "backtrace.h"
#include "backtrace_fake.h"
#include "debug_log.h"
@ -57,3 +59,31 @@ void backtrace_log(const uintptr_t* frames, size_t frame_count) {
error_log(" #%02zd pc %p", i, reinterpret_cast<void*>(frames[i]));
}
}
static std::deque<std::vector<unwindstack::LocalFrameData>> g_fake_local_frame_data;
void BacktraceUnwindFakeClearAll() {
g_fake_local_frame_data.clear();
}
void BacktraceUnwindFake(const std::vector<unwindstack::LocalFrameData>& frames) {
g_fake_local_frame_data.push_back(frames);
}
bool Unwind(std::vector<uintptr_t>* frames, std::vector<unwindstack::LocalFrameData>* info, size_t) {
if (g_fake_local_frame_data.empty()) {
return false;
}
*info = g_fake_local_frame_data.front();
g_fake_local_frame_data.pop_front();
frames->clear();
for (const auto& frame : *info) {
frames->push_back(frame.pc);
}
return true;
}
void UnwindLog(const std::vector<unwindstack::LocalFrameData>& /*frame_info*/) {
}

View file

@ -21,7 +21,12 @@
#include <vector>
#include <unwindstack/LocalUnwinder.h>
void backtrace_fake_clear_all();
void backtrace_fake_add(const std::vector<uintptr_t>& ips);
void BacktraceUnwindFakeClearAll();
void BacktraceUnwindFake(const std::vector<unwindstack::LocalFrameData>& frames);
#endif // MALLOC_DEBUG_TESTS_BACKTRACE_FAKE_H

View file

@ -45,9 +45,7 @@ std::string getFakeLogPrint() {
}
extern "C" int async_safe_format_log(int priority, const char* tag, const char* format, ...) {
g_fake_log_print += std::to_string(priority) + ' ';
g_fake_log_print += tag;
g_fake_log_print += ' ';
g_fake_log_print += std::to_string(priority) + ' ' + tag + ' ';
va_list ap;
va_start(ap, format);
@ -59,6 +57,12 @@ extern "C" int async_safe_format_log(int priority, const char* tag, const char*
return 0;
}
extern "C" int async_safe_write_log(int priority, const char* tag, const char* msg) {
g_fake_log_print += std::to_string(priority) + ' ' + tag + ' ' + msg + '\n';
return 0;
}
extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
g_fake_log_buf += tag;

View file

@ -83,6 +83,11 @@ std::string usage_string(
"6 malloc_debug backtrace_dump_prefix.<PID>.final.txt.\n"
"6 malloc_debug The default is false.\n"
"6 malloc_debug \n"
"6 malloc_debug backtrace_full\n"
"6 malloc_debug Any time a backtrace is acquired, use an unwinder that can\n"
"6 malloc_debug display Java stack frames. This unwinder can run slower than\n"
"6 malloc_debug normal unwinder.\n"
"6 malloc_debug \n"
"6 malloc_debug fill_on_alloc[=XX]\n"
"6 malloc_debug On first allocation, fill with the value 0xeb.\n"
"6 malloc_debug If XX is set it will only fill up to XX bytes of the\n"
@ -413,6 +418,24 @@ TEST_F(MallocDebugConfigTest, backtrace_dump_prefix) {
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
TEST_F(MallocDebugConfigTest, backtrace_full) {
ASSERT_TRUE(InitConfig("backtrace_full")) << getFakeLogPrint();
ASSERT_EQ(BACKTRACE_FULL, config->options());
ASSERT_STREQ("", getFakeLogBuf().c_str());
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
TEST_F(MallocDebugConfigTest, backtrace_full_fail) {
ASSERT_FALSE(InitConfig("backtrace_full=200")) << getFakeLogPrint();
ASSERT_STREQ("", getFakeLogBuf().c_str());
std::string log_msg(
"6 malloc_debug malloc_testing: value set for option 'backtrace_full' "
"which does not take a value\n");
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
}
TEST_F(MallocDebugConfigTest, fill_on_alloc) {
ASSERT_TRUE(InitConfig("fill_on_alloc=64")) << getFakeLogPrint();
ASSERT_EQ(FILL_ON_ALLOC, config->options());

View file

@ -19,6 +19,7 @@
#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>
@ -1311,30 +1312,23 @@ void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
"Android Native Heap Dump v1.0\n"
"\n"
"Total memory: 405\n"
"Allocation records: 6\n"
"Backtrace size: 4\n"
"\n"
#if defined(__LP64__)
"z 0 sz 50 num 1 bt 000000000000a100 000000000000b200\n"
"z 0 sz 10 num 1 bt 000000000000a000 000000000000b000\n"
"z 0 sz 5 num 1 bt 000000000000a300 000000000000b300\n"
"z 1 sz 200 num 1 bt 0000000000000500 0000000000000600\n"
"z 1 sz 100 num 1 bt 0000000000000100 0000000000000200\n"
"z 1 sz 40 num 1 bt 0000000000000300 0000000000000400\n"
#else
"z 0 sz 50 num 1 bt 0000a100 0000b200\n"
"z 0 sz 10 num 1 bt 0000a000 0000b000\n"
"z 0 sz 5 num 1 bt 0000a300 0000b300\n"
"z 1 sz 200 num 1 bt 00000500 00000600\n"
"z 1 sz 100 num 1 bt 00000100 00000200\n"
"z 1 sz 40 num 1 bt 00000300 00000400\n"
#endif
"MAPS\n"
"MAP_DATA\n"
"END\n\n";
R"(Android Native Heap Dump v1.1
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());
@ -1383,24 +1377,20 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit) {
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
"Android Native Heap Dump v1.0\n"
"\n"
"Total memory: 1200\n"
"Allocation records: 3\n"
"Backtrace size: 4\n"
"\n"
#if defined(__LP64__)
"z 0 sz 500 num 1 bt 000000000000a000 000000000000b000 000000000000c000\n"
"z 0 sz 400 num 1 bt 000000000000a000 000000000000b000\n"
"z 0 sz 300 num 1 bt 0000000000000100 0000000000000200\n"
#else
"z 0 sz 500 num 1 bt 0000a000 0000b000 0000c000\n"
"z 0 sz 400 num 1 bt 0000a000 0000b000\n"
"z 0 sz 300 num 1 bt 00000100 00000200\n"
#endif
"MAPS\n"
"MAP_DATA\n"
"END\n\n";
R"(Android Native Heap Dump v1.1
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());
@ -1436,28 +1426,84 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit_shared_backtrace) {
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
"Android Native Heap Dump v1.0\n"
"\n"
"Total memory: 1000\n"
"Allocation records: 2\n"
"Backtrace size: 4\n"
"\n"
#if defined(__LP64__)
"z 0 sz 400 num 1 bt 000000000000a000 000000000000b000 000000000000c000\n"
"z 0 sz 300 num 2 bt 0000000000000100 0000000000000200\n"
#else
"z 0 sz 400 num 1 bt 0000a000 0000b000 0000c000\n"
"z 0 sz 300 num 2 bt 00000100 00000200\n"
#endif
"MAPS\n"
"MAP_DATA\n"
"END\n\n";
R"(Android Native Heap Dump v1.1
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) {
Init("backtrace=4 backtrace_full backtrace_dump_on_exit");
BacktraceUnwindFake(
std::vector<unwindstack::LocalFrameData>{{nullptr, 0x1100, 0x100, "fake1", 10},
{nullptr, 0x1200, 0x200, "fake2", 20}});
unwindstack::MapInfo map_info{0x10000, 0x20000, 0, PROT_READ | PROT_EXEC, "/data/fake.so"};
BacktraceUnwindFake(
std::vector<unwindstack::LocalFrameData>{{&map_info, 0x1a000, 0xa000, "level1", 0},
{&map_info, 0x1b000, 0xb000, "level2", 10}});
BacktraceUnwindFake(
std::vector<unwindstack::LocalFrameData>{{nullptr, 0x1a000, 0xa000, "func1", 0},
{nullptr, 0x1b000, 0xb000, "func2", 10},
{nullptr, 0x1c000, 0xc000, "", 30}});
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.1
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");
@ -2286,7 +2332,8 @@ TEST_F(MallocDebugTest, verify_pointers) {
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 failed to get any frames.\n");
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;