Merge "Replace libbacktrace with libunwindstack directly." am: 8926f1a945
am: 1783fa1940
Change-Id: Ia55d280485efffe88309d3eb2d3123c19b9c1066
This commit is contained in:
commit
c2ad098718
12 changed files with 725 additions and 537 deletions
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
40
debuggerd/libdebuggerd/test/UnwinderMock.h
Normal file
40
debuggerd/libdebuggerd/test/UnwinderMock.h
Normal 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);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue