Merge "Replace libbacktrace with libunwindstack directly."

This commit is contained in:
Christopher Ferris 2019-01-31 04:24:18 +00:00 committed by Gerrit Code Review
commit 8926f1a945
12 changed files with 725 additions and 537 deletions

View file

@ -114,7 +114,6 @@ cc_library_static {
"libasync_safe",
"libbase",
"libdebuggerd",
"libbacktrace",
"libunwindstack",
"libdexfile", // libunwindstack dependency
"libdexfile_external", // libunwindstack dependency
@ -124,7 +123,6 @@ cc_library_static {
],
target: {
recovery: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
exclude_static_libs: [
"libartbase",
"libdexfile",
@ -164,7 +162,6 @@ cc_library_static {
srcs: [
"libdebuggerd/backtrace.cpp",
"libdebuggerd/elf_utils.cpp",
"libdebuggerd/open_files_list.cpp",
"libdebuggerd/tombstone.cpp",
"libdebuggerd/utility.cpp",
@ -177,7 +174,6 @@ cc_library_static {
include_dirs: ["bionic/libc"],
static_libs: [
"libbacktrace",
"libdexfile_external", // libunwindstack dependency
"libdexfile_support", // libunwindstack dependency
"libunwindstack",
@ -223,7 +219,6 @@ cc_test {
},
shared_libs: [
"libbacktrace",
"libbase",
"libcutils",
"libdebuggerd_client",
@ -291,7 +286,6 @@ cc_binary {
],
shared_libs: [
"libbacktrace",
"libbase",
"liblog",
"libprocinfo",

View file

@ -48,7 +48,12 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/tombstone.h"
@ -63,8 +68,6 @@
using android::base::unique_fd;
using android::base::StringPrintf;
using unwindstack::Regs;
static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
struct stat st;
std::string task_path = StringPrintf("task/%d", tid);
@ -287,7 +290,8 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
case 1:
*abort_msg_address = crash_info->data.v1.abort_msg_address;
*siginfo = crash_info->data.v1.siginfo;
regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->data.v1.ucontext));
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
&crash_info->data.v1.ucontext));
break;
default:
@ -469,7 +473,7 @@ int main(int argc, char** argv) {
info.siginfo = &siginfo;
info.signo = info.siginfo->si_signo;
} else {
info.registers.reset(Regs::RemoteGet(thread));
info.registers.reset(unwindstack::Regs::RemoteGet(thread));
if (!info.registers) {
PLOG(WARNING) << "failed to fetch registers for thread " << thread;
ptrace(PTRACE_DETACH, thread, 0, 0);
@ -562,30 +566,25 @@ int main(int argc, char** argv) {
}
// TODO: Use seccomp to lock ourselves down.
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(vm_pid, false));
if (!map) {
LOG(FATAL) << "failed to create backtrace map";
}
std::shared_ptr<unwindstack::Memory> process_memory = map->GetProcessMemory();
if (!process_memory) {
LOG(FATAL) << "failed to get unwindstack::Memory handle";
unwindstack::UnwinderFromPid unwinder(256, vm_pid);
if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
LOG(FATAL) << "Failed to init unwinder object.";
}
std::string amfd_data;
if (backtrace) {
ATRACE_NAME("dump_backtrace");
dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread);
dump_backtrace(std::move(g_output_fd), &unwinder, thread_info, g_target_thread);
} else {
{
ATRACE_NAME("fdsan table dump");
populate_fdsan_table(&open_files, process_memory, fdsan_table_address);
populate_fdsan_table(&open_files, unwinder.GetProcessMemory(), fdsan_table_address);
}
{
ATRACE_NAME("engrave_tombstone");
engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info,
g_target_thread, abort_msg_address, &open_files, &amfd_data);
engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread,
abort_msg_address, &open_files, &amfd_data);
}
}

View file

@ -42,9 +42,12 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <backtrace/BacktraceMap.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "debuggerd/handler.h"
#include "handler/fallback.h"
@ -55,7 +58,6 @@
#include "libdebuggerd/tombstone.h"
using android::base::unique_fd;
using unwindstack::Regs;
extern "C" bool __linker_enable_fallback_allocator();
extern "C" void __linker_disable_fallback_allocator();
@ -73,17 +75,22 @@ static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {
}
{
std::unique_ptr<Regs> regs;
std::unique_ptr<unwindstack::Regs> regs;
ThreadInfo thread;
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
// TODO: Create this once and store it in a global?
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
dump_backtrace_thread(output_fd, map.get(), thread);
unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
if (unwinder.Init(arch)) {
dump_backtrace_thread(output_fd, &unwinder, thread);
} else {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
}
}
__linker_disable_fallback_allocator();
}

View file

@ -35,8 +35,8 @@
#include <string>
#include <android-base/unique_fd.h>
#include <backtrace/Backtrace.h>
#include <log/log.h>
#include <unwindstack/Unwinder.h>
#include "libdebuggerd/types.h"
#include "libdebuggerd/utility.h"
@ -59,25 +59,27 @@ static void dump_process_footer(log_t* log, pid_t pid) {
_LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
}
void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) {
void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
const ThreadInfo& thread) {
log_t log;
log.tfd = output_fd;
log.amfd_data = nullptr;
_LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
std::vector<backtrace_frame_data_t> frames;
if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) {
unwinder->SetRegs(thread.registers.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
_LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid);
return;
}
for (auto& frame : frames) {
_LOG(&log, logtype::BACKTRACE, " %s\n", Backtrace::FormatFrameData(&frame).c_str());
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(&log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(i).c_str());
}
}
void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
log_t log;
log.tfd = output_fd.get();
@ -91,10 +93,10 @@ void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
dump_process_header(&log, target->second.pid, target->second.process_name.c_str());
dump_backtrace_thread(output_fd.get(), map, target->second);
dump_backtrace_thread(output_fd.get(), unwinder, target->second);
for (const auto& [tid, info] : thread_info) {
if (tid != target_thread) {
dump_backtrace_thread(output_fd.get(), map, info);
dump_backtrace_thread(output_fd.get(), unwinder, info);
}
}

View file

@ -1,121 +0,0 @@
/*
* 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.
*/
#define LOG_TAG "DEBUG"
#include "libdebuggerd/elf_utils.h"
#include <elf.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <android-base/stringprintf.h>
#include <log/log.h>
#include <unwindstack/Memory.h>
#define NOTE_ALIGN(size) (((size) + 3) & ~3)
template <typename HdrType, typename PhdrType, typename NhdrType>
static bool get_build_id(unwindstack::Memory* memory, uintptr_t base_addr, uint8_t* e_ident,
std::string* build_id) {
HdrType hdr;
memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT);
// First read the rest of the header.
if (memory->Read(base_addr + EI_NIDENT, reinterpret_cast<uint8_t*>(&hdr) + EI_NIDENT,
sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) {
return false;
}
for (size_t i = 0; i < hdr.e_phnum; i++) {
PhdrType phdr;
if (memory->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize,
reinterpret_cast<uint8_t*>(&phdr), sizeof(phdr)) != sizeof(phdr)) {
return false;
}
// Looking for the .note.gnu.build-id note.
if (phdr.p_type == PT_NOTE) {
size_t hdr_size = phdr.p_filesz;
uintptr_t addr = base_addr + phdr.p_offset;
while (hdr_size >= sizeof(NhdrType)) {
NhdrType nhdr;
if (memory->Read(addr, reinterpret_cast<uint8_t*>(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) {
return false;
}
addr += sizeof(nhdr);
if (nhdr.n_type == NT_GNU_BUILD_ID) {
// Skip the name (which is the owner and should be "GNU").
addr += NOTE_ALIGN(nhdr.n_namesz);
uint8_t build_id_data[160];
if (nhdr.n_descsz > sizeof(build_id_data)) {
ALOGE("Possible corrupted note, desc size value is too large: %u",
nhdr.n_descsz);
return false;
}
if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
return false;
}
build_id->clear();
for (size_t bytes = 0; bytes < nhdr.n_descsz; bytes++) {
*build_id += android::base::StringPrintf("%02x", build_id_data[bytes]);
}
return true;
} else {
// Move past the extra note data.
hdr_size -= sizeof(nhdr);
size_t skip_bytes = NOTE_ALIGN(nhdr.n_namesz) + NOTE_ALIGN(nhdr.n_descsz);
addr += skip_bytes;
if (hdr_size < skip_bytes) {
break;
}
hdr_size -= skip_bytes;
}
}
}
}
return false;
}
bool elf_get_build_id(unwindstack::Memory* memory, uintptr_t addr, std::string* build_id) {
// Read and verify the elf magic number first.
uint8_t e_ident[EI_NIDENT];
if (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) {
return false;
}
if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
return false;
}
// Read the rest of EI_NIDENT.
if (memory->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) {
return false;
}
if (e_ident[EI_CLASS] == ELFCLASS32) {
return get_build_id<Elf32_Ehdr, Elf32_Phdr, Elf32_Nhdr>(memory, addr, e_ident, build_id);
} else if (e_ident[EI_CLASS] == ELFCLASS64) {
return get_build_id<Elf64_Ehdr, Elf64_Phdr, Elf64_Nhdr>(memory, addr, e_ident, build_id);
}
return false;
}

View file

@ -28,15 +28,19 @@
#include "types.h"
#include "utility.h"
class BacktraceMap;
// Forward delcaration
namespace unwindstack {
class Unwinder;
}
// Dumps a backtrace using a format similar to what Dalvik uses so that the result
// can be intermixed in a bug report.
void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map,
void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
void dump_backtrace_header(int output_fd);
void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread);
void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
const ThreadInfo& thread);
void dump_backtrace_footer(int output_fd);
#endif // _DEBUGGERD_BACKTRACE_H

View file

@ -1,29 +0,0 @@
/*
* 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.
*/
#ifndef _DEBUGGERD_ELF_UTILS_H
#define _DEBUGGERD_ELF_UTILS_H
#include <stdint.h>
#include <string>
namespace unwindstack {
class Memory;
}
bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*);
#endif // _DEBUGGERD_ELF_UTILS_H

View file

@ -29,7 +29,13 @@
#include "open_files_list.h"
#include "types.h"
class BacktraceMap;
// Forward declarations
namespace unwindstack {
class Unwinder;
}
// The maximum number of frames to save when unwinding.
constexpr size_t kMaxFrames = 256;
/* Create and open a tombstone file for writing.
* Returns a writable file descriptor, or -1 with errno set appropriately.
@ -38,16 +44,15 @@ class BacktraceMap;
int open_tombstone(std::string* path);
/* Creates a tombstone file and writes the crash dump to it. */
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files,
pid_t pid, pid_t tid, const std::string& process_name,
const std::map<pid_t, std::string>& threads, uint64_t abort_msg_address,
std::string* amfd_data);
void engrave_tombstone(int tombstone_fd, unwindstack::Unwinder* unwinder,
const OpenFilesList* open_files, pid_t pid, pid_t tid,
const std::string& process_name, const std::map<pid_t, std::string>& threads,
uint64_t abort_msg_address, std::string* amfd_data);
void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, siginfo_t* siginfo,
ucontext_t* ucontext);
void engrave_tombstone(android::base::unique_fd output_fd, BacktraceMap* map,
unwindstack::Memory* process_memory,
void engrave_tombstone(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
std::string* amfd_data);

View file

@ -1,32 +0,0 @@
/*
* 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.
*/
#ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H
#define _DEBUGGERD_TEST_BACKTRACE_MOCK_H
#include <backtrace/BacktraceMap.h>
class BacktraceMapMock : public BacktraceMap {
public:
BacktraceMapMock() : BacktraceMap(0) {}
virtual ~BacktraceMapMock() {}
void AddMap(backtrace_map_t& map) {
maps_.push_back(map);
}
};
#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H

View file

@ -0,0 +1,40 @@
/*
* 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.
*/
#pragma once
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Unwinder.h>
class UnwinderMock : public unwindstack::Unwinder {
public:
UnwinderMock() : Unwinder(128, new unwindstack::Maps, nullptr) {}
virtual ~UnwinderMock() { delete GetMaps(); }
void MockAddMap(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, std::string name,
uint64_t load_bias) {
GetMaps()->Add(start, end, offset, flags, name, load_bias);
}
void MockSetBuildID(uint64_t offset, const std::string& build_id) {
unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
if (map_info != nullptr) {
std::string* new_build_id = new std::string(build_id);
map_info->build_id = reinterpret_cast<uintptr_t>(new_build_id);
}
}
};

View file

@ -26,26 +26,16 @@
#include "libdebuggerd/utility.h"
#include "BacktraceMock.h"
#include "elf_fake.h"
#include "UnwinderMock.h"
#include "host_signal_fixup.h"
#include "log_fake.h"
#include "tombstone.cpp"
void dump_registers(log_t*, pid_t) {
}
void dump_memory_and_code(log_t*, Backtrace*) {
}
void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
}
class TombstoneTest : public ::testing::Test {
protected:
virtual void SetUp() {
map_mock_.reset(new BacktraceMapMock());
unwinder_mock_.reset(new UnwinderMock());
char tmp_file[256];
const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
@ -71,7 +61,6 @@ class TombstoneTest : public ::testing::Test {
log_.should_retrieve_logcat = false;
resetLogs();
elf_set_fake_build_id("");
}
virtual void TearDown() {
@ -80,24 +69,20 @@ class TombstoneTest : public ::testing::Test {
}
}
std::unique_ptr<BacktraceMapMock> map_mock_;
std::unique_ptr<UnwinderMock> unwinder_mock_;
log_t log_;
std::string amfd_data_;
};
TEST_F(TombstoneTest, single_map) {
backtrace_map_t map;
#if defined(__LP64__)
map.start = 0x123456789abcd000UL;
map.end = 0x123456789abdf000UL;
unwinder_mock_->MockAddMap(0x123456789abcd000UL, 0x123456789abdf000UL, 0, 0, "", 0);
#else
map.start = 0x1234000;
map.end = 0x1235000;
unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, 0, "", 0);
#endif
map_mock_->AddMap(map);
dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
dump_all_maps(&log_, unwinder_mock_.get(), 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -119,20 +104,25 @@ TEST_F(TombstoneTest, single_map) {
}
TEST_F(TombstoneTest, single_map_elf_build_id) {
backtrace_map_t map;
uint64_t build_id_offset;
#if defined(__LP64__)
map.start = 0x123456789abcd000UL;
map.end = 0x123456789abdf000UL;
build_id_offset = 0x123456789abcd000UL;
unwinder_mock_->MockAddMap(build_id_offset, 0x123456789abdf000UL, 0, PROT_READ,
"/system/lib/libfake.so", 0);
#else
map.start = 0x1234000;
map.end = 0x1235000;
build_id_offset = 0x1234000;
unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, PROT_READ, "/system/lib/libfake.so", 0);
#endif
map.flags = PROT_READ;
map.name = "/system/lib/libfake.so";
map_mock_->AddMap(map);
elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
unwinder_mock_->MockSetBuildID(
build_id_offset,
std::string{static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef),
static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56),
static_cast<char>(0x78), static_cast<char>(0x90), static_cast<char>(0xab),
static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12),
static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78),
static_cast<char>(0x90)});
dump_all_maps(&log_, unwinder_mock_.get(), 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -153,83 +143,15 @@ TEST_F(TombstoneTest, single_map_elf_build_id) {
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
// Even though build id is present, it should not be printed in either of
// these cases.
TEST_F(TombstoneTest, single_map_no_build_id) {
backtrace_map_t map;
#if defined(__LP64__)
map.start = 0x123456789abcd000UL;
map.end = 0x123456789abdf000UL;
#else
map.start = 0x1234000;
map.end = 0x1235000;
#endif
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.name = "/system/lib/libfake.so";
map_mock_->AddMap(map);
elf_set_fake_build_id("abcdef1234567890abcdef1234567890");
dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
"\nmemory map (2 entries):\n"
#if defined(__LP64__)
" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n"
" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n";
#else
" 01234000-01234fff -w- 0 1000\n"
" 01234000-01234fff -w- 0 1000 /system/lib/libfake.so\n";
#endif
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
ASSERT_STREQ("", amfd_data_.c_str());
// Verify that the log buf is empty, and no error messages.
ASSERT_STREQ("", getFakeLogBuf().c_str());
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
TEST_F(TombstoneTest, multiple_maps) {
backtrace_map_t map;
unwinder_mock_->MockAddMap(0xa234000, 0xa235000, 0, 0, "", 0);
unwinder_mock_->MockAddMap(0xa334000, 0xa335000, 0xf000, PROT_READ, "", 0);
unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/system/lib/fake.so", 0);
map.start = 0xa234000;
map.end = 0xa235000;
map_mock_->AddMap(map);
map.start = 0xa334000;
map.end = 0xa335000;
map.offset = 0xf000;
map.flags = PROT_READ;
map_mock_->AddMap(map);
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
dump_all_maps(&log_, map_mock_.get(), nullptr, 0);
dump_all_maps(&log_, unwinder_mock_.get(), 0);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -259,31 +181,12 @@ TEST_F(TombstoneTest, multiple_maps) {
}
TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
backtrace_map_t map;
unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/system/lib/fake.so", 0);
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
dump_all_maps(&log_, map_mock_.get(), nullptr, 0x1000);
dump_all_maps(&log_, unwinder_mock_.get(), 0x1000);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -311,31 +214,12 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
}
TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
backtrace_map_t map;
unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/system/lib/fake.so", 0);
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa533000);
dump_all_maps(&log_, unwinder_mock_.get(), 0xa533000);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -363,31 +247,12 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
}
TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
backtrace_map_t map;
unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/system/lib/fake.so", 0);
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa534040);
dump_all_maps(&log_, unwinder_mock_.get(), 0xa534040);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -413,36 +278,17 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
}
TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
backtrace_map_t map;
map.start = 0xa434000;
map.end = 0xa435000;
map.offset = 0x1000;
map.load_bias = 0xd000;
map.flags = PROT_WRITE;
map_mock_->AddMap(map);
map.start = 0xa534000;
map.end = 0xa535000;
map.offset = 0x3000;
map.load_bias = 0x2000;
map.flags = PROT_EXEC;
map_mock_->AddMap(map);
map.start = 0xa634000;
map.end = 0xa635000;
map.offset = 0;
map.load_bias = 0;
map.flags = PROT_READ | PROT_WRITE | PROT_EXEC;
map.name = "/system/lib/fake.so";
map_mock_->AddMap(map);
unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
"/system/lib/fake.so", 0);
#if defined(__LP64__)
uint64_t addr = 0x12345a534040UL;
#else
uint64_t addr = 0xf534040UL;
#endif
dump_all_maps(&log_, map_mock_.get(), nullptr, addr);
dump_all_maps(&log_, unwinder_mock_.get(), addr);
std::string tombstone_contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
@ -502,3 +348,467 @@ TEST_F(TombstoneTest, dump_timestamp) {
dump_timestamp(&log_, 0);
ASSERT_STREQ("Timestamp: 1970-01-01 00:00:00+0000\n", amfd_data_.c_str());
}
class MemoryPattern : public unwindstack::Memory {
public:
MemoryPattern() = default;
virtual ~MemoryPattern() = default;
size_t Read(uint64_t, void* dst, size_t size) override {
uint8_t* data = reinterpret_cast<uint8_t*>(dst);
for (size_t i = 0; i < size; i++) {
data[i] = (i % 0xff);
}
return size;
}
};
TEST_F(TombstoneTest, dump_stack_single_frame) {
std::vector<unwindstack::FrameData> frames;
unwindstack::Maps maps;
MemoryPattern memory;
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
dump_stack(&log_, frames, &maps, &memory);
std::string contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
std::string expected =
#if defined(__LP64__)
" 0000000000001f80 0706050403020100\n"
" 0000000000001f88 0f0e0d0c0b0a0908\n"
" 0000000000001f90 1716151413121110\n"
" 0000000000001f98 1f1e1d1c1b1a1918\n"
" 0000000000001fa0 2726252423222120\n"
" 0000000000001fa8 2f2e2d2c2b2a2928\n"
" 0000000000001fb0 3736353433323130\n"
" 0000000000001fb8 3f3e3d3c3b3a3938\n"
" 0000000000001fc0 4746454443424140\n"
" 0000000000001fc8 4f4e4d4c4b4a4948\n"
" 0000000000001fd0 5756555453525150\n"
" 0000000000001fd8 5f5e5d5c5b5a5958\n"
" 0000000000001fe0 6766656463626160\n"
" 0000000000001fe8 6f6e6d6c6b6a6968\n"
" 0000000000001ff0 7776757473727170\n"
" 0000000000001ff8 7f7e7d7c7b7a7978\n"
" #00 0000000000002000 0706050403020100\n"
" 0000000000002008 0f0e0d0c0b0a0908\n"
" 0000000000002010 1716151413121110\n"
" 0000000000002018 1f1e1d1c1b1a1918\n"
" 0000000000002020 2726252423222120\n"
" 0000000000002028 2f2e2d2c2b2a2928\n"
" 0000000000002030 3736353433323130\n"
" 0000000000002038 3f3e3d3c3b3a3938\n"
" 0000000000002040 4746454443424140\n"
" 0000000000002048 4f4e4d4c4b4a4948\n"
" 0000000000002050 5756555453525150\n"
" 0000000000002058 5f5e5d5c5b5a5958\n"
" 0000000000002060 6766656463626160\n"
" 0000000000002068 6f6e6d6c6b6a6968\n"
" 0000000000002070 7776757473727170\n"
" 0000000000002078 7f7e7d7c7b7a7978\n";
#else
" 00001fc0 03020100\n"
" 00001fc4 07060504\n"
" 00001fc8 0b0a0908\n"
" 00001fcc 0f0e0d0c\n"
" 00001fd0 13121110\n"
" 00001fd4 17161514\n"
" 00001fd8 1b1a1918\n"
" 00001fdc 1f1e1d1c\n"
" 00001fe0 23222120\n"
" 00001fe4 27262524\n"
" 00001fe8 2b2a2928\n"
" 00001fec 2f2e2d2c\n"
" 00001ff0 33323130\n"
" 00001ff4 37363534\n"
" 00001ff8 3b3a3938\n"
" 00001ffc 3f3e3d3c\n"
" #00 00002000 03020100\n"
" 00002004 07060504\n"
" 00002008 0b0a0908\n"
" 0000200c 0f0e0d0c\n"
" 00002010 13121110\n"
" 00002014 17161514\n"
" 00002018 1b1a1918\n"
" 0000201c 1f1e1d1c\n"
" 00002020 23222120\n"
" 00002024 27262524\n"
" 00002028 2b2a2928\n"
" 0000202c 2f2e2d2c\n"
" 00002030 33323130\n"
" 00002034 37363534\n"
" 00002038 3b3a3938\n"
" 0000203c 3f3e3d3c\n";
#endif
EXPECT_EQ(expected, contents);
}
TEST_F(TombstoneTest, dump_stack_multiple_frames_same_sp) {
std::vector<unwindstack::FrameData> frames;
unwindstack::Maps maps;
MemoryPattern memory;
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2000});
dump_stack(&log_, frames, &maps, &memory);
std::string contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
std::string expected =
#if defined(__LP64__)
" 0000000000001f80 0706050403020100\n"
" 0000000000001f88 0f0e0d0c0b0a0908\n"
" 0000000000001f90 1716151413121110\n"
" 0000000000001f98 1f1e1d1c1b1a1918\n"
" 0000000000001fa0 2726252423222120\n"
" 0000000000001fa8 2f2e2d2c2b2a2928\n"
" 0000000000001fb0 3736353433323130\n"
" 0000000000001fb8 3f3e3d3c3b3a3938\n"
" 0000000000001fc0 4746454443424140\n"
" 0000000000001fc8 4f4e4d4c4b4a4948\n"
" 0000000000001fd0 5756555453525150\n"
" 0000000000001fd8 5f5e5d5c5b5a5958\n"
" 0000000000001fe0 6766656463626160\n"
" 0000000000001fe8 6f6e6d6c6b6a6968\n"
" 0000000000001ff0 7776757473727170\n"
" 0000000000001ff8 7f7e7d7c7b7a7978\n"
" #00 0000000000002000 0706050403020100\n"
" ................ ................\n"
" #01 0000000000002000 0706050403020100\n"
" 0000000000002008 0f0e0d0c0b0a0908\n"
" 0000000000002010 1716151413121110\n"
" 0000000000002018 1f1e1d1c1b1a1918\n"
" 0000000000002020 2726252423222120\n"
" 0000000000002028 2f2e2d2c2b2a2928\n"
" 0000000000002030 3736353433323130\n"
" 0000000000002038 3f3e3d3c3b3a3938\n"
" 0000000000002040 4746454443424140\n"
" 0000000000002048 4f4e4d4c4b4a4948\n"
" 0000000000002050 5756555453525150\n"
" 0000000000002058 5f5e5d5c5b5a5958\n"
" 0000000000002060 6766656463626160\n"
" 0000000000002068 6f6e6d6c6b6a6968\n"
" 0000000000002070 7776757473727170\n"
" 0000000000002078 7f7e7d7c7b7a7978\n";
#else
" 00001fc0 03020100\n"
" 00001fc4 07060504\n"
" 00001fc8 0b0a0908\n"
" 00001fcc 0f0e0d0c\n"
" 00001fd0 13121110\n"
" 00001fd4 17161514\n"
" 00001fd8 1b1a1918\n"
" 00001fdc 1f1e1d1c\n"
" 00001fe0 23222120\n"
" 00001fe4 27262524\n"
" 00001fe8 2b2a2928\n"
" 00001fec 2f2e2d2c\n"
" 00001ff0 33323130\n"
" 00001ff4 37363534\n"
" 00001ff8 3b3a3938\n"
" 00001ffc 3f3e3d3c\n"
" #00 00002000 03020100\n"
" ........ ........\n"
" #01 00002000 03020100\n"
" 00002004 07060504\n"
" 00002008 0b0a0908\n"
" 0000200c 0f0e0d0c\n"
" 00002010 13121110\n"
" 00002014 17161514\n"
" 00002018 1b1a1918\n"
" 0000201c 1f1e1d1c\n"
" 00002020 23222120\n"
" 00002024 27262524\n"
" 00002028 2b2a2928\n"
" 0000202c 2f2e2d2c\n"
" 00002030 33323130\n"
" 00002034 37363534\n"
" 00002038 3b3a3938\n"
" 0000203c 3f3e3d3c\n";
#endif
EXPECT_EQ(expected, contents);
}
TEST_F(TombstoneTest, dump_stack_multiple_frames) {
std::vector<unwindstack::FrameData> frames;
unwindstack::Maps maps;
MemoryPattern memory;
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2100});
dump_stack(&log_, frames, &maps, &memory);
std::string contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
std::string expected =
#if defined(__LP64__)
" 0000000000001f80 0706050403020100\n"
" 0000000000001f88 0f0e0d0c0b0a0908\n"
" 0000000000001f90 1716151413121110\n"
" 0000000000001f98 1f1e1d1c1b1a1918\n"
" 0000000000001fa0 2726252423222120\n"
" 0000000000001fa8 2f2e2d2c2b2a2928\n"
" 0000000000001fb0 3736353433323130\n"
" 0000000000001fb8 3f3e3d3c3b3a3938\n"
" 0000000000001fc0 4746454443424140\n"
" 0000000000001fc8 4f4e4d4c4b4a4948\n"
" 0000000000001fd0 5756555453525150\n"
" 0000000000001fd8 5f5e5d5c5b5a5958\n"
" 0000000000001fe0 6766656463626160\n"
" 0000000000001fe8 6f6e6d6c6b6a6968\n"
" 0000000000001ff0 7776757473727170\n"
" 0000000000001ff8 7f7e7d7c7b7a7978\n"
" #00 0000000000002000 0706050403020100\n"
" 0000000000002008 0f0e0d0c0b0a0908\n"
" #01 0000000000002010 0706050403020100\n"
" 0000000000002018 0f0e0d0c0b0a0908\n"
" 0000000000002020 1716151413121110\n"
" 0000000000002028 1f1e1d1c1b1a1918\n"
" 0000000000002030 2726252423222120\n"
" 0000000000002038 2f2e2d2c2b2a2928\n"
" 0000000000002040 3736353433323130\n"
" 0000000000002048 3f3e3d3c3b3a3938\n"
" 0000000000002050 4746454443424140\n"
" 0000000000002058 4f4e4d4c4b4a4948\n"
" 0000000000002060 5756555453525150\n"
" 0000000000002068 5f5e5d5c5b5a5958\n"
" 0000000000002070 6766656463626160\n"
" 0000000000002078 6f6e6d6c6b6a6968\n"
" 0000000000002080 7776757473727170\n"
" 0000000000002088 7f7e7d7c7b7a7978\n"
" ................ ................\n"
" #02 0000000000002100 0706050403020100\n"
" 0000000000002108 0f0e0d0c0b0a0908\n"
" 0000000000002110 1716151413121110\n"
" 0000000000002118 1f1e1d1c1b1a1918\n"
" 0000000000002120 2726252423222120\n"
" 0000000000002128 2f2e2d2c2b2a2928\n"
" 0000000000002130 3736353433323130\n"
" 0000000000002138 3f3e3d3c3b3a3938\n"
" 0000000000002140 4746454443424140\n"
" 0000000000002148 4f4e4d4c4b4a4948\n"
" 0000000000002150 5756555453525150\n"
" 0000000000002158 5f5e5d5c5b5a5958\n"
" 0000000000002160 6766656463626160\n"
" 0000000000002168 6f6e6d6c6b6a6968\n"
" 0000000000002170 7776757473727170\n"
" 0000000000002178 7f7e7d7c7b7a7978\n";
#else
" 00001fc0 03020100\n"
" 00001fc4 07060504\n"
" 00001fc8 0b0a0908\n"
" 00001fcc 0f0e0d0c\n"
" 00001fd0 13121110\n"
" 00001fd4 17161514\n"
" 00001fd8 1b1a1918\n"
" 00001fdc 1f1e1d1c\n"
" 00001fe0 23222120\n"
" 00001fe4 27262524\n"
" 00001fe8 2b2a2928\n"
" 00001fec 2f2e2d2c\n"
" 00001ff0 33323130\n"
" 00001ff4 37363534\n"
" 00001ff8 3b3a3938\n"
" 00001ffc 3f3e3d3c\n"
" #00 00002000 03020100\n"
" 00002004 07060504\n"
" 00002008 0b0a0908\n"
" 0000200c 0f0e0d0c\n"
" #01 00002010 03020100\n"
" 00002014 07060504\n"
" 00002018 0b0a0908\n"
" 0000201c 0f0e0d0c\n"
" 00002020 13121110\n"
" 00002024 17161514\n"
" 00002028 1b1a1918\n"
" 0000202c 1f1e1d1c\n"
" 00002030 23222120\n"
" 00002034 27262524\n"
" 00002038 2b2a2928\n"
" 0000203c 2f2e2d2c\n"
" 00002040 33323130\n"
" 00002044 37363534\n"
" 00002048 3b3a3938\n"
" 0000204c 3f3e3d3c\n"
" ........ ........\n"
" #02 00002100 03020100\n"
" 00002104 07060504\n"
" 00002108 0b0a0908\n"
" 0000210c 0f0e0d0c\n"
" 00002110 13121110\n"
" 00002114 17161514\n"
" 00002118 1b1a1918\n"
" 0000211c 1f1e1d1c\n"
" 00002120 23222120\n"
" 00002124 27262524\n"
" 00002128 2b2a2928\n"
" 0000212c 2f2e2d2c\n"
" 00002130 33323130\n"
" 00002134 37363534\n"
" 00002138 3b3a3938\n"
" 0000213c 3f3e3d3c\n";
#endif
EXPECT_EQ(expected, contents);
}
TEST_F(TombstoneTest, dump_stack_multiple_frames_disjoint_frames) {
std::vector<unwindstack::FrameData> frames;
unwindstack::Maps maps;
MemoryPattern memory;
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1000, .pc = 0x301000, .sp = 0x2000});
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x2010});
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1000});
frames.push_back(
unwindstack::FrameData{.num = 0, .rel_pc = 0x1400, .pc = 0x301400, .sp = 0x1030});
dump_stack(&log_, frames, &maps, &memory);
std::string contents;
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &contents));
std::string expected =
#if defined(__LP64__)
" 0000000000001f80 0706050403020100\n"
" 0000000000001f88 0f0e0d0c0b0a0908\n"
" 0000000000001f90 1716151413121110\n"
" 0000000000001f98 1f1e1d1c1b1a1918\n"
" 0000000000001fa0 2726252423222120\n"
" 0000000000001fa8 2f2e2d2c2b2a2928\n"
" 0000000000001fb0 3736353433323130\n"
" 0000000000001fb8 3f3e3d3c3b3a3938\n"
" 0000000000001fc0 4746454443424140\n"
" 0000000000001fc8 4f4e4d4c4b4a4948\n"
" 0000000000001fd0 5756555453525150\n"
" 0000000000001fd8 5f5e5d5c5b5a5958\n"
" 0000000000001fe0 6766656463626160\n"
" 0000000000001fe8 6f6e6d6c6b6a6968\n"
" 0000000000001ff0 7776757473727170\n"
" 0000000000001ff8 7f7e7d7c7b7a7978\n"
" #00 0000000000002000 0706050403020100\n"
" 0000000000002008 0f0e0d0c0b0a0908\n"
" #01 0000000000002010 0706050403020100\n"
" 0000000000002018 0f0e0d0c0b0a0908\n"
" 0000000000002020 1716151413121110\n"
" 0000000000002028 1f1e1d1c1b1a1918\n"
" 0000000000002030 2726252423222120\n"
" 0000000000002038 2f2e2d2c2b2a2928\n"
" 0000000000002040 3736353433323130\n"
" 0000000000002048 3f3e3d3c3b3a3938\n"
" 0000000000002050 4746454443424140\n"
" 0000000000002058 4f4e4d4c4b4a4948\n"
" 0000000000002060 5756555453525150\n"
" 0000000000002068 5f5e5d5c5b5a5958\n"
" 0000000000002070 6766656463626160\n"
" 0000000000002078 6f6e6d6c6b6a6968\n"
" 0000000000002080 7776757473727170\n"
" 0000000000002088 7f7e7d7c7b7a7978\n"
" ................ ................\n"
" #02 0000000000001000 0706050403020100\n"
" 0000000000001008 0f0e0d0c0b0a0908\n"
" 0000000000001010 1716151413121110\n"
" 0000000000001018 1f1e1d1c1b1a1918\n"
" 0000000000001020 2726252423222120\n"
" 0000000000001028 2f2e2d2c2b2a2928\n"
" #03 0000000000001030 0706050403020100\n"
" 0000000000001038 0f0e0d0c0b0a0908\n"
" 0000000000001040 1716151413121110\n"
" 0000000000001048 1f1e1d1c1b1a1918\n"
" 0000000000001050 2726252423222120\n"
" 0000000000001058 2f2e2d2c2b2a2928\n"
" 0000000000001060 3736353433323130\n"
" 0000000000001068 3f3e3d3c3b3a3938\n"
" 0000000000001070 4746454443424140\n"
" 0000000000001078 4f4e4d4c4b4a4948\n"
" 0000000000001080 5756555453525150\n"
" 0000000000001088 5f5e5d5c5b5a5958\n"
" 0000000000001090 6766656463626160\n"
" 0000000000001098 6f6e6d6c6b6a6968\n"
" 00000000000010a0 7776757473727170\n"
" 00000000000010a8 7f7e7d7c7b7a7978\n";
#else
" 00001fc0 03020100\n"
" 00001fc4 07060504\n"
" 00001fc8 0b0a0908\n"
" 00001fcc 0f0e0d0c\n"
" 00001fd0 13121110\n"
" 00001fd4 17161514\n"
" 00001fd8 1b1a1918\n"
" 00001fdc 1f1e1d1c\n"
" 00001fe0 23222120\n"
" 00001fe4 27262524\n"
" 00001fe8 2b2a2928\n"
" 00001fec 2f2e2d2c\n"
" 00001ff0 33323130\n"
" 00001ff4 37363534\n"
" 00001ff8 3b3a3938\n"
" 00001ffc 3f3e3d3c\n"
" #00 00002000 03020100\n"
" 00002004 07060504\n"
" 00002008 0b0a0908\n"
" 0000200c 0f0e0d0c\n"
" #01 00002010 03020100\n"
" 00002014 07060504\n"
" 00002018 0b0a0908\n"
" 0000201c 0f0e0d0c\n"
" 00002020 13121110\n"
" 00002024 17161514\n"
" 00002028 1b1a1918\n"
" 0000202c 1f1e1d1c\n"
" 00002030 23222120\n"
" 00002034 27262524\n"
" 00002038 2b2a2928\n"
" 0000203c 2f2e2d2c\n"
" 00002040 33323130\n"
" 00002044 37363534\n"
" 00002048 3b3a3938\n"
" 0000204c 3f3e3d3c\n"
" ........ ........\n"
" #02 00001000 03020100\n"
" 00001004 07060504\n"
" 00001008 0b0a0908\n"
" 0000100c 0f0e0d0c\n"
" 00001010 13121110\n"
" 00001014 17161514\n"
" 00001018 1b1a1918\n"
" 0000101c 1f1e1d1c\n"
" 00001020 23222120\n"
" 00001024 27262524\n"
" 00001028 2b2a2928\n"
" 0000102c 2f2e2d2c\n"
" #03 00001030 03020100\n"
" 00001034 07060504\n"
" 00001038 0b0a0908\n"
" 0000103c 0f0e0d0c\n"
" 00001040 13121110\n"
" 00001044 17161514\n"
" 00001048 1b1a1918\n"
" 0000104c 1f1e1d1c\n"
" 00001050 23222120\n"
" 00001054 27262524\n"
" 00001058 2b2a2928\n"
" 0000105c 2f2e2d2c\n"
" 00001060 33323130\n"
" 00001064 37363534\n"
" 00001068 3b3a3938\n"
" 0000106c 3f3e3d3c\n";
#endif
EXPECT_EQ(expected, contents);
}

View file

@ -41,19 +41,20 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/log.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
#include <log/log.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
// Needed to get DEBUGGER_SIGNAL.
#include "debuggerd/handler.h"
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/elf_utils.h"
#include "libdebuggerd/open_files_list.h"
#include "libdebuggerd/utility.h"
@ -62,9 +63,6 @@ using android::base::GetProperty;
using android::base::StringPrintf;
using android::base::unique_fd;
using unwindstack::Memory;
using unwindstack::Regs;
using namespace std::literals::string_literals;
#define STACK_WORDS 16
@ -87,7 +85,7 @@ static void dump_timestamp(log_t* log, time_t time) {
_LOG(log, logtype::HEADER, "Timestamp: %s\n", buf);
}
static void dump_probable_cause(log_t* log, const siginfo_t* si, BacktraceMap* map) {
static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps) {
std::string cause;
if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
if (si->si_addr < reinterpret_cast<void*>(4096)) {
@ -104,12 +102,9 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, BacktraceMap* m
cause = "call to kuser_cmpxchg64";
}
} else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
for (auto it = map->begin(); it != map->end(); ++it) {
const backtrace_map_t* entry = *it;
if (si->si_addr >= reinterpret_cast<void*>(entry->start) &&
si->si_addr < reinterpret_cast<void*>(entry->end) && entry->flags == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
}
unwindstack::MapInfo* map_info = maps->Find(reinterpret_cast<uint64_t>(si->si_addr));
if (map_info != nullptr && map_info->flags == PROT_EXEC) {
cause = "execute-only (no-read) memory access error; likely due to data in .text.";
}
} else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@ -119,7 +114,8 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, BacktraceMap* m
if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
}
static void dump_signal_info(log_t* log, const ThreadInfo& thread_info, Memory* process_memory) {
static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
unwindstack::Memory* process_memory) {
char addr_desc[64]; // ", fault addr 0x1234"
if (signal_has_si_addr(thread_info.siginfo)) {
void* addr = thread_info.siginfo->si_addr;
@ -156,14 +152,14 @@ static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
}
static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
static void dump_stack_segment(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
uint64_t* sp, size_t words, int label) {
// Read the data all at once.
word_t stack_data[words];
// TODO: Do we need to word align this for crashes caused by a misaligned sp?
// The process_vm_readv implementation of Memory should handle this appropriately?
size_t bytes_read = process_memory->Read(*sp, stack_data, sizeof(word_t) * words);
size_t bytes_read = memory->Read(*sp, stack_data, sizeof(word_t) * words);
words = bytes_read / sizeof(word_t);
std::string line;
for (size_t i = 0; i < words; i++) {
@ -176,17 +172,15 @@ static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory*
}
line += StringPrintf("%" PRIPTR " %" PRIPTR, *sp, static_cast<uint64_t>(stack_data[i]));
backtrace_map_t map;
backtrace_map->FillIn(stack_data[i], &map);
std::string map_name{map.Name()};
if (BacktraceMap::IsValid(map) && !map_name.empty()) {
line += " " + map_name;
uint64_t offset = 0;
std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset);
if (!func_name.empty()) {
unwindstack::MapInfo* map_info = maps->Find(stack_data[i]);
if (map_info != nullptr && !map_info->name.empty()) {
line += " " + map_info->name;
std::string func_name;
uint64_t func_offset = 0;
if (map_info->GetFunctionName(stack_data[i], &func_name, &func_offset)) {
line += " (" + func_name;
if (offset) {
line += StringPrintf("+%" PRIu64, offset);
if (func_offset) {
line += StringPrintf("+%" PRIu64, func_offset);
}
line += ')';
}
@ -197,12 +191,11 @@ static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory*
}
}
static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory,
std::vector<backtrace_frame_data_t>& frames) {
static void dump_stack(log_t* log, const std::vector<unwindstack::FrameData>& frames,
unwindstack::Maps* maps, unwindstack::Memory* memory) {
size_t first = 0, last;
for (size_t i = 0; i < frames.size(); i++) {
const backtrace_frame_data_t& frame = frames[i];
if (frame.sp) {
if (frames[i].sp) {
if (!first) {
first = i+1;
}
@ -217,29 +210,44 @@ static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_
// Dump a few words before the first frame.
uint64_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t);
dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1);
dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, -1);
#if defined(__LP64__)
static constexpr const char delimiter[] = " ................ ................\n";
#else
static constexpr const char delimiter[] = " ........ ........\n";
#endif
// Dump a few words from all successive frames.
// Only log the first 3 frames, put the rest in the tombstone.
for (size_t i = first; i <= last; i++) {
const backtrace_frame_data_t* frame = &frames[i];
auto* frame = &frames[i];
if (sp != frame->sp) {
_LOG(log, logtype::STACK, " ........ ........\n");
_LOG(log, logtype::STACK, delimiter);
sp = frame->sp;
}
if (i == last) {
dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i);
if (sp < frame->sp + frame->stack_size) {
_LOG(log, logtype::STACK, " ........ ........\n");
}
} else {
size_t words = frame->stack_size / sizeof(word_t);
if (words == 0) {
words = 1;
} else if (words > STACK_WORDS) {
if (i != last) {
// Print stack data up to the stack from the next frame.
size_t words;
uint64_t next_sp = frames[i + 1].sp;
if (next_sp < sp) {
// The next frame is probably using a completely different stack,
// so dump the max from this stack.
words = STACK_WORDS;
} else {
words = (next_sp - sp) / sizeof(word_t);
if (words == 0) {
// The sp is the same as the next frame, print at least
// one line for this frame.
words = 1;
} else if (words > STACK_WORDS) {
words = STACK_WORDS;
}
}
dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i);
dump_stack_segment(log, maps, memory, &sp, words, i);
} else {
// Print some number of words past the last stack frame since we
// don't know how large the stack is.
dump_stack_segment(log, maps, memory, &sp, STACK_WORDS, i);
}
}
}
@ -256,7 +264,7 @@ static std::string get_addr_string(uint64_t addr) {
return addr_str;
}
static void dump_abort_message(log_t* log, Memory* process_memory, uint64_t address) {
static void dump_abort_message(log_t* log, unwindstack::Memory* process_memory, uint64_t address) {
if (address == 0) {
return;
}
@ -285,16 +293,16 @@ static void dump_abort_message(log_t* log, Memory* process_memory, uint64_t addr
_LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
}
static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uint64_t addr) {
static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t addr) {
bool print_fault_address_marker = addr;
ScopedBacktraceMapIteratorLock lock(map);
unwindstack::Maps* maps = unwinder->GetMaps();
_LOG(log, logtype::MAPS,
"\n"
"memory map (%zu entr%s):",
map->size(), map->size() == 1 ? "y" : "ies");
maps->Total(), maps->Total() == 1 ? "y" : "ies");
if (print_fault_address_marker) {
if (map->begin() != map->end() && addr < (*map->begin())->start) {
if (maps->Total() != 0 && addr < maps->Get(0)->start) {
_LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
@ -305,51 +313,54 @@ static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory,
_LOG(log, logtype::MAPS, "\n");
}
std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
std::string line;
for (auto it = map->begin(); it != map->end(); ++it) {
const backtrace_map_t* entry = *it;
for (unwindstack::MapInfo* map_info : *maps) {
line = " ";
if (print_fault_address_marker) {
if (addr < entry->start) {
if (addr < map_info->start) {
_LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
} else if (addr >= entry->start && addr < entry->end) {
} else if (addr >= map_info->start && addr < map_info->end) {
line = "--->";
print_fault_address_marker = false;
}
}
line += get_addr_string(entry->start) + '-' + get_addr_string(entry->end - 1) + ' ';
if (entry->flags & PROT_READ) {
line += get_addr_string(map_info->start) + '-' + get_addr_string(map_info->end - 1) + ' ';
if (map_info->flags & PROT_READ) {
line += 'r';
} else {
line += '-';
}
if (entry->flags & PROT_WRITE) {
if (map_info->flags & PROT_WRITE) {
line += 'w';
} else {
line += '-';
}
if (entry->flags & PROT_EXEC) {
if (map_info->flags & PROT_EXEC) {
line += 'x';
} else {
line += '-';
}
line += StringPrintf(" %8" PRIx64 " %8" PRIx64, entry->offset, entry->end - entry->start);
line += StringPrintf(" %8" PRIx64 " %8" PRIx64, map_info->offset,
map_info->end - map_info->start);
bool space_needed = true;
if (entry->name.length() > 0) {
if (!map_info->name.empty()) {
space_needed = false;
line += " " + entry->name;
std::string build_id;
if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) {
line += " " + map_info->name;
std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
line += " (BuildId: " + build_id + ")";
}
}
if (entry->load_bias != 0) {
uint64_t load_bias = map_info->GetLoadBias(process_memory);
if (load_bias != 0) {
if (space_needed) {
line += ' ';
}
line += StringPrintf(" (load bias 0x%" PRIx64 ")", entry->load_bias);
line += StringPrintf(" (load bias 0x%" PRIx64 ")", load_bias);
}
_LOG(log, logtype::MAPS, "%s\n", line.c_str());
}
@ -359,9 +370,9 @@ static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory,
}
}
void dump_backtrace(log_t* log, std::vector<backtrace_frame_data_t>& frames, const char* prefix) {
for (auto& frame : frames) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, Backtrace::FormatFrameData(&frame).c_str());
void dump_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
for (size_t i = 0; i < unwinder->NumFrames(); i++) {
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
}
@ -377,7 +388,7 @@ static void print_register_row(log_t* log,
_LOG(log, logtype::REGISTERS, " %s\n", output.c_str());
}
void dump_registers(log_t* log, Regs* regs) {
void dump_registers(log_t* log, unwindstack::Regs* regs) {
// Split lr/sp/pc into their own special row.
static constexpr size_t column_count = 4;
std::vector<std::pair<std::string, uint64_t>> current_row;
@ -416,23 +427,22 @@ void dump_registers(log_t* log, Regs* regs) {
print_register_row(log, special_row);
}
void dump_memory_and_code(log_t* log, BacktraceMap* map, Memory* memory, Regs* regs) {
regs->IterateRegisters([log, map, memory](const char* reg_name, uint64_t reg_value) {
void dump_memory_and_code(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
unwindstack::Regs* regs) {
regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
std::string label{"memory near "s + reg_name};
if (map) {
backtrace_map_t map_info;
map->FillIn(reg_value, &map_info);
std::string map_name{map_info.Name()};
if (!map_name.empty()) label += " (" + map_info.Name() + ")";
if (maps) {
unwindstack::MapInfo* map_info = maps->Find(reg_value);
if (map_info != nullptr && !map_info->name.empty()) {
label += " (" + map_info->name + ")";
}
}
dump_memory(log, memory, reg_value, label);
});
}
static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory,
const ThreadInfo& thread_info, uint64_t abort_msg_address,
bool primary_thread) {
UNUSED(process_memory);
static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
uint64_t abort_msg_address, bool primary_thread) {
log->current_tid = thread_info.tid;
if (!primary_thread) {
_LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
@ -440,41 +450,41 @@ static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory,
dump_thread_info(log, thread_info);
if (thread_info.siginfo) {
dump_signal_info(log, thread_info, process_memory);
dump_probable_cause(log, thread_info.siginfo, map);
dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
dump_probable_cause(log, thread_info.siginfo, unwinder->GetMaps());
}
if (primary_thread) {
dump_abort_message(log, process_memory, abort_msg_address);
dump_abort_message(log, unwinder->GetProcessMemory().get(), abort_msg_address);
}
dump_registers(log, thread_info.registers.get());
// Unwind will mutate the registers, so make a copy first.
std::unique_ptr<Regs> regs_copy(thread_info.registers->Clone());
std::vector<backtrace_frame_data_t> frames;
if (!Backtrace::Unwind(regs_copy.get(), map, &frames, 0, nullptr)) {
std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
unwinder->SetRegs(regs_copy.get());
unwinder->Unwind();
if (unwinder->NumFrames() == 0) {
_LOG(log, logtype::THREAD, "Failed to unwind");
return false;
}
if (!frames.empty()) {
} else {
_LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
dump_backtrace(log, frames, " ");
dump_backtrace(log, unwinder, " ");
_LOG(log, logtype::STACK, "\nstack:\n");
dump_stack(log, map, process_memory, frames);
dump_stack(log, unwinder->frames(), unwinder->GetMaps(), unwinder->GetProcessMemory().get());
}
if (primary_thread) {
dump_memory_and_code(log, map, process_memory, thread_info.registers.get());
if (map) {
unwindstack::Maps* maps = unwinder->GetMaps();
dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
thread_info.registers.get());
if (maps != nullptr) {
uint64_t addr = 0;
siginfo_t* si = thread_info.siginfo;
if (signal_has_si_addr(si)) {
addr = reinterpret_cast<uint64_t>(si->si_addr);
}
dump_all_maps(log, map, process_memory, addr);
dump_all_maps(log, unwinder, addr);
}
}
@ -625,7 +635,8 @@ void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, si
read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), "<unknown>");
read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), "<unknown>");
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext));
std::unique_ptr<unwindstack::Regs> regs(
unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
std::map<pid_t, ThreadInfo> threads;
threads[gettid()] = ThreadInfo{
@ -637,18 +648,16 @@ void engrave_tombstone_ucontext(int tombstone_fd, uint64_t abort_msg_address, si
.siginfo = siginfo,
};
std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(getpid(), false));
if (!backtrace_map) {
ALOGE("failed to create backtrace map");
_exit(1);
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
LOG(FATAL) << "Failed to init unwinder object.";
}
std::shared_ptr<Memory> process_memory = backtrace_map->GetProcessMemory();
engrave_tombstone(unique_fd(dup(tombstone_fd)), backtrace_map.get(), process_memory.get(),
threads, tid, abort_msg_address, nullptr, nullptr);
engrave_tombstone(unique_fd(dup(tombstone_fd)), &unwinder, threads, tid, abort_msg_address,
nullptr, nullptr);
}
void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory,
void engrave_tombstone(unique_fd output_fd, unwindstack::Unwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
uint64_t abort_msg_address, OpenFilesList* open_files,
std::string* amfd_data) {
@ -669,7 +678,7 @@ void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_m
if (it == threads.end()) {
LOG(FATAL) << "failed to find target thread";
}
dump_thread(&log, map, process_memory, it->second, abort_msg_address, true);
dump_thread(&log, unwinder, it->second, abort_msg_address, true);
if (want_logs) {
dump_logs(&log, it->second.pid, 50);
@ -680,7 +689,7 @@ void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_m
continue;
}
dump_thread(&log, map, process_memory, thread_info, 0, false);
dump_thread(&log, unwinder, thread_info, 0, false);
}
if (open_files) {