Merge "Adapt to versioned_ndk_headers renaming."
diff --git a/libc/Android.bp b/libc/Android.bp
index 6e3b5cd..d3d162b 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1772,8 +1772,8 @@
arch: {
arm: {
//TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
// Don't re-export new/delete and friends, even if the compiler really wants to.
version_script: "libc.arm.map",
@@ -1806,8 +1806,8 @@
},
x86: {
//TODO: This is to work around b/24465209. Remove after root cause is fixed
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
// Don't re-export new/delete and friends, even if the compiler really wants to.
version_script: "libc.x86.map",
@@ -1838,8 +1838,8 @@
//TODO (dimitry): This is to work around b/24465209. Remove after root cause is fixed
arch: {
arm: {
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
version_script: "libstdc++.arm.map",
},
arm64: {
@@ -1852,8 +1852,8 @@
version_script: "libstdc++.mips64.map",
},
x86: {
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
version_script: "libstdc++.x86.map",
},
x86_64: {
diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp
index 1a79802..a4ff5d4 100644
--- a/libc/malloc_debug/Android.bp
+++ b/libc/malloc_debug/Android.bp
@@ -14,6 +14,7 @@
stl: "libc++_static",
whole_static_libs: [
+ "libbase",
"libasync_safe",
"libdemangle",
],
@@ -49,6 +50,7 @@
"malloc_debug.cpp",
"PointerData.cpp",
"RecordData.cpp",
+ "UnwindBacktrace.cpp",
],
stl: "libc++_static",
@@ -69,9 +71,14 @@
static_libs: [
"libasync_safe",
"libbase",
+ "libdemangle",
"libc_malloc_debug_backtrace",
],
+ shared_libs: [
+ "libunwindstack",
+ ],
+
multilib: {
lib32: {
version_script: "exported32.map",
@@ -112,15 +119,21 @@
"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",
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 2c94fe8..96fc27f 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -93,6 +93,9 @@
{
"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 @@
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");
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index 3bcef0b..86d1ee4 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -43,6 +43,7 @@
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.
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
index f9a2dca..debc14e 100644
--- a/libc/malloc_debug/GuardData.cpp
+++ b/libc/malloc_debug/GuardData.cpp
@@ -62,10 +62,7 @@
}
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);
}
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
index 85139e6..b0e2fc8 100644
--- a/libc/malloc_debug/PointerData.cpp
+++ b/libc/malloc_debug/PointerData.cpp
@@ -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::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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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");
+ }
}
}
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index 7a0b3b8..62d4186 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -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 @@
size_t size;
bool zygote_child_alloc;
FrameInfoType* frame_info;
+ std::vector<unwindstack::LocalFrameData>* backtrace_info;
};
class PointerData : public OptionData {
@@ -160,6 +163,7 @@
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 @@
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_;
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index 51540f8..b50de40 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -150,8 +150,8 @@
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 @@
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 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
diff --git a/libc/malloc_debug/UnwindBacktrace.cpp b/libc/malloc_debug/UnwindBacktrace.cpp
new file mode 100644
index 0000000..ddafc73
--- /dev/null
+++ b/libc/malloc_debug/UnwindBacktrace.cpp
@@ -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());
+ }
+}
diff --git a/libc/malloc_debug/UnwindBacktrace.h b/libc/malloc_debug/UnwindBacktrace.h
new file mode 100644
index 0000000..4c6c8d4
--- /dev/null
+++ b/libc/malloc_debug/UnwindBacktrace.h
@@ -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);
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 6f841ca..836c33b 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -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 @@
});
}
+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 @@
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 @@
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);
diff --git a/libc/malloc_debug/malloc_debug.h b/libc/malloc_debug/malloc_debug.h
index ac210e2..7280ea7 100644
--- a/libc/malloc_debug/malloc_debug.h
+++ b/libc/malloc_debug/malloc_debug.h
@@ -62,3 +62,5 @@
constexpr size_t FREE_TRACK_MEM_BUFFER_SIZE = 4096;
extern const MallocDispatch* g_dispatch;
+
+void BacktraceAndLog();
diff --git a/libc/malloc_debug/tests/backtrace_fake.cpp b/libc/malloc_debug/tests/backtrace_fake.cpp
index db542e5..ad16c02 100644
--- a/libc/malloc_debug/tests/backtrace_fake.cpp
+++ b/libc/malloc_debug/tests/backtrace_fake.cpp
@@ -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 @@
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*/) {
+}
diff --git a/libc/malloc_debug/tests/backtrace_fake.h b/libc/malloc_debug/tests/backtrace_fake.h
index f2aa7a0..a9ee97d 100644
--- a/libc/malloc_debug/tests/backtrace_fake.h
+++ b/libc/malloc_debug/tests/backtrace_fake.h
@@ -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
diff --git a/libc/malloc_debug/tests/log_fake.cpp b/libc/malloc_debug/tests/log_fake.cpp
index fb64e3e..9a23ac7 100644
--- a/libc/malloc_debug/tests/log_fake.cpp
+++ b/libc/malloc_debug/tests/log_fake.cpp
@@ -45,9 +45,7 @@
}
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 @@
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;
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index 4603535..2a6e399 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -83,6 +83,11 @@
"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 @@
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());
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 1504d06..0663f6a 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -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 @@
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 @@
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 @@
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 @@
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;
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 9d97957..d5f8e70 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -49,8 +49,8 @@
arch: {
arm: {
version_script: "libdl.arm.map",
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
},
arm64: {
version_script: "libdl.arm64.map",
@@ -62,11 +62,11 @@
version_script: "libdl.mips64.map",
},
x86: {
+ pack_relocations: false,
ldflags: [
"-Wl,--exclude-libs=libgcc_eh.a",
"-Wl,--hash-style=both",
],
- use_clang_lld: false,
version_script: "libdl.x86.map",
},
x86_64: {
diff --git a/libm/Android.bp b/libm/Android.bp
index 76bbca3..9566d5f 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -293,8 +293,8 @@
],
},
instruction_set: "arm",
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
version_script: "libm.arm.map",
},
@@ -421,8 +421,8 @@
],
},
local_include_dirs: ["i387"],
+ pack_relocations: false,
ldflags: ["-Wl,--hash-style=both"],
- use_clang_lld: false,
version_script: "libm.x86.map",
},
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 7d9abf9..6d68497 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1432,10 +1432,13 @@
}
// Duplicate these definitions here because they are android specific
-// note that we cannot include <elf.h> because #defines conflict with
-// enum names provided by LLVM.
-#define DT_ANDROID_REL (llvm::ELF::DT_LOOS + 2)
-#define DT_ANDROID_RELA (llvm::ELF::DT_LOOS + 4)
+// - note that we cannot include <elf.h> because #defines conflict with
+// enum names provided by LLVM.
+// - we also don't use llvm::ELF::DT_LOOS because its value is 0x60000000
+// rather than the 0x6000000d we expect
+#define DT_LOOS 0x6000000d
+#define DT_ANDROID_REL (DT_LOOS + 2)
+#define DT_ANDROID_RELA (DT_LOOS + 4)
template<typename ELFT>
void validate_compatibility_of_native_library(const std::string& soname,