Use new unwinder for offline in libbacktrace.

libbbacktrace changes:
- Completely rewrite the BacktraceOffline class to use the new unwinder.
- Modify the test data to save ucontext_t data instead of unw_context data.
- Convert the previous tests from unw_context data to ucontext_t data.

Bug: 65682279

Test: New unit tests pass in libunwindstack.
Test: All offline tests continue to pass.
Change-Id: I540345c304b20199d46deeb0349a0638a0f3ab2f
This commit is contained in:
Christopher Ferris 2017-12-11 17:44:33 -08:00
parent 8abe4e2638
commit c8bec5aa91
26 changed files with 424 additions and 1513 deletions

View file

@ -128,39 +128,8 @@ cc_library_shared {
cflags: ["-O0"],
srcs: ["backtrace_testlib.cpp"],
target: {
linux: {
shared_libs: [
"libunwind",
"libunwindstack",
],
},
}
}
//-------------------------------------------------------------------------
// The libbacktrace_offline static library.
//-------------------------------------------------------------------------
cc_library_static {
name: "libbacktrace_offline",
defaults: ["libbacktrace_common"],
host_supported: true,
srcs: ["BacktraceOffline.cpp"],
cflags: [
"-D__STDC_CONSTANT_MACROS",
"-D__STDC_LIMIT_MACROS",
"-D__STDC_FORMAT_MACROS",
],
header_libs: ["llvm-headers"],
// Use shared libraries so their headers get included during build.
shared_libs = [
"libbase",
"libunwind",
"libunwindstack",
"libziparchive",
shared_libs: [
"libunwindstack",
],
}
@ -193,28 +162,11 @@ cc_test {
"libbase",
"libcutils",
"liblog",
"libunwind",
"libunwindstack",
],
group_static_libs: true,
// Statically link LLVMlibraries to remove dependency on llvm shared library.
static_libs = [
"libbacktrace_offline",
"libLLVMObject",
"libLLVMBitReader",
"libLLVMMC",
"libLLVMMCParser",
"libLLVMCore",
"libLLVMSupport",
"libziparchive",
"libz",
],
header_libs: ["llvm-headers"],
target: {
android: {
cflags: ["-DENABLE_PSS_TESTS"],
@ -223,9 +175,6 @@ cc_test {
],
},
linux_glibc: {
host_ldlibs: [
"-lncurses",
],
static_libs: ["libutils"],
},
},

View file

@ -168,5 +168,9 @@ std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
return "Failed to find a function in debug sections";
case BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED:
return "Failed to execute dwarf instructions in debug sections";
case BACKTRACE_UNWIND_ERROR_UNWIND_INFO:
return "Failed to unwind due to invalid unwind information";
case BACKTRACE_UNWIND_ERROR_REPEATED_FRAME:
return "Failed to unwind due to same sp/pc repeating";
}
}

View file

@ -64,7 +64,7 @@ size_t BacktraceCurrent::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
return bytes;
}
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, void* ucontext) {
if (GetMap() == nullptr) {
// Without a map object, we can't do anything.
error_.error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;

View file

@ -19,7 +19,6 @@
#include <stdint.h>
#include <sys/types.h>
#include <ucontext.h>
#include <backtrace/Backtrace.h>
@ -44,7 +43,7 @@ class BacktraceCurrent : public Backtrace {
bool ReadWord(uint64_t ptr, word_t* out_value) override;
bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override;
bool Unwind(size_t num_ignore_frames, void* ucontext) override;
protected:
bool DiscardFrame(const backtrace_frame_data_t& frame);
@ -52,7 +51,7 @@ class BacktraceCurrent : public Backtrace {
private:
bool UnwindThread(size_t num_ignore_frames);
virtual bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) = 0;
virtual bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) = 0;
};
#endif // _LIBBACKTRACE_BACKTRACE_CURRENT_H

View file

@ -146,13 +146,3 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
return map;
}
#endif
BacktraceMap* BacktraceMap::Create(pid_t pid, const std::vector<backtrace_map_t>& maps) {
BacktraceMap* backtrace_map = new BacktraceMap(pid);
backtrace_map->maps_.insert(backtrace_map->maps_.begin(), maps.begin(), maps.end());
std::sort(backtrace_map->maps_.begin(), backtrace_map->maps_.end(),
[](const backtrace_map_t& map1, const backtrace_map_t& map2) {
return map1.start < map2.start;
});
return backtrace_map;
}

File diff suppressed because it is too large Load diff

View file

@ -1,90 +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 _LIBBACKTRACE_UNWIND_OFFLINE_H
#define _LIBBACKTRACE_UNWIND_OFFLINE_H
#include <libunwind.h>
#include <stdint.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unordered_map>
#include <unordered_set>
#include <backtrace/Backtrace.h>
struct Space {
uint64_t start;
uint64_t end;
const uint8_t* data;
Space() { Clear(); }
void Clear();
size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
};
struct DebugFrameInfo;
class BacktraceOffline : public Backtrace {
public:
BacktraceOffline(pid_t pid, pid_t tid, BacktraceMap* map, const backtrace_stackinfo_t& stack,
bool cache_file)
: Backtrace(pid, tid, map),
cache_file_(cache_file),
context_(nullptr),
is_debug_frame_used_(false) {
stack_space_.start = stack.start;
stack_space_.end = stack.end;
stack_space_.data = stack.data;
}
virtual ~BacktraceOffline() = default;
bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
bool ReadWord(uint64_t ptr, word_t* out_value) override;
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
bool FindProcInfo(unw_addr_space_t addr_space, uint64_t ip, unw_proc_info_t* proc_info,
int need_unwind_info);
bool ReadReg(size_t reg_index, uint64_t* value);
protected:
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
DebugFrameInfo* GetDebugFrameInFile(const std::string& filename);
bool cache_file_;
ucontext_t* context_;
Space eh_frame_hdr_space_;
Space eh_frame_space_;
Space arm_extab_space_;
Space arm_exidx_space_;
Space stack_space_;
// is_debug_frame_used_ is to make sure we can try both .debug_frame and .ARM.exidx in
// FindProcInfo() on ARM. One example is EsxContext::Clear() in
// vendor/lib/egl/libGLESv2_adreno.so. EsxContext::Clear() appears in both .debug_frame and
// .ARM.exidx. However, libunwind fails to execute debug_frame instruction
// "DW_CFA_offset_extended: r265 at cfa-48". So we need to try .ARM.exidx to unwind that
// function.
bool is_debug_frame_used_;
};
#endif // _LIBBACKTRACE_BACKTRACE_OFFLINE_H

View file

@ -18,7 +18,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <memory>
#include <set>
@ -103,7 +102,7 @@ static void FillInDexFrame(UnwindStackMap* stack_map, uint64_t dex_pc,
bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names) {
std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
auto process_memory = stack_map->process_memory();
unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
@ -112,6 +111,38 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
}
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
if (error != nullptr) {
switch (unwinder.LastErrorCode()) {
case unwindstack::ERROR_NONE:
error->error_code = BACKTRACE_UNWIND_NO_ERROR;
break;
case unwindstack::ERROR_MEMORY_INVALID:
error->error_code = BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED;
error->error_info.addr = unwinder.LastErrorAddress();
break;
case unwindstack::ERROR_UNWIND_INFO:
error->error_code = BACKTRACE_UNWIND_ERROR_UNWIND_INFO;
break;
case unwindstack::ERROR_UNSUPPORTED:
error->error_code = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
break;
case unwindstack::ERROR_INVALID_MAP:
error->error_code = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
break;
case unwindstack::ERROR_MAX_FRAMES_EXCEEDED:
error->error_code = BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT;
break;
case unwindstack::ERROR_REPEATED_FRAME:
error->error_code = BACKTRACE_UNWIND_ERROR_REPEATED_FRAME;
break;
}
}
if (num_ignore_frames >= unwinder.NumFrames()) {
frames->resize(0);
@ -178,7 +209,7 @@ std::string UnwindStackCurrent::GetFunctionNameRaw(uint64_t pc, uint64_t* offset
return GetMap()->GetFunctionName(pc, offset);
}
bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, void* ucontext) {
std::unique_ptr<unwindstack::Regs> regs;
if (ucontext == nullptr) {
regs.reset(unwindstack::Regs::CreateFromLocal());
@ -189,9 +220,8 @@ bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t*
regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
}
error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names);
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names, &error_);
}
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
@ -201,7 +231,7 @@ std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset)
return GetMap()->GetFunctionName(pc, offset);
}
bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, void* context) {
std::unique_ptr<unwindstack::Regs> regs;
if (context == nullptr) {
regs.reset(unwindstack::Regs::RemoteGet(Tid()));
@ -209,10 +239,77 @@ bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), context));
}
error_.error_code = BACKTRACE_UNWIND_NO_ERROR;
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr);
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
}
size_t UnwindStackPtrace::Read(uint64_t addr, uint8_t* buffer, size_t bytes) {
return memory_.Read(addr, buffer, bytes);
}
UnwindStackOffline::UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map,
bool map_shared)
: Backtrace(pid, tid, map), arch_(arch) {
map_shared_ = map_shared;
}
bool UnwindStackOffline::Unwind(size_t num_ignore_frames, void* ucontext) {
if (ucontext == nullptr) {
return false;
}
unwindstack::ArchEnum arch;
switch (arch_) {
case ARCH_ARM:
arch = unwindstack::ARCH_ARM;
break;
case ARCH_ARM64:
arch = unwindstack::ARCH_ARM64;
break;
case ARCH_X86:
arch = unwindstack::ARCH_X86;
break;
case ARCH_X86_64:
arch = unwindstack::ARCH_X86_64;
break;
default:
return false;
}
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, nullptr, &error_);
}
std::string UnwindStackOffline::GetFunctionNameRaw(uint64_t, uint64_t*) {
return "";
}
size_t UnwindStackOffline::Read(uint64_t, uint8_t*, size_t) {
return 0;
}
bool UnwindStackOffline::ReadWord(uint64_t, word_t*) {
return false;
}
Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
const std::vector<backtrace_map_t>& maps,
const backtrace_stackinfo_t& stack) {
BacktraceMap* map = BacktraceMap::CreateOffline(pid, maps, stack);
if (map == nullptr) {
return nullptr;
}
return new UnwindStackOffline(arch, pid, tid, map, false);
}
Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
if (map == nullptr) {
return nullptr;
}
return new UnwindStackOffline(arch, pid, tid, map, true);
}
void Backtrace::SetGlobalElfCache(bool enable) {
unwindstack::Elf::SetCachingEnabled(enable);
}

View file

@ -34,7 +34,7 @@ class UnwindStackCurrent : public BacktraceCurrent {
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
bool UnwindFromContext(size_t num_ignore_frames, void* ucontext) override;
};
class UnwindStackPtrace : public BacktracePtrace {
@ -42,9 +42,9 @@ class UnwindStackPtrace : public BacktracePtrace {
UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
virtual ~UnwindStackPtrace() = default;
bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
bool Unwind(size_t num_ignore_frames, void* context) override;
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset);
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset) override;
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
@ -52,4 +52,20 @@ class UnwindStackPtrace : public BacktracePtrace {
unwindstack::MemoryRemote memory_;
};
class UnwindStackOffline : public Backtrace {
public:
UnwindStackOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map, bool map_shared);
bool Unwind(size_t num_ignore_frames, void* context) override;
std::string GetFunctionNameRaw(uint64_t pc, uint64_t* offset);
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
bool ReadWord(uint64_t ptr, word_t* out_value) override;
private:
ArchEnum arch_;
};
#endif // _LIBBACKTRACE_UNWIND_STACK_H

View file

@ -147,6 +147,43 @@ UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t dex_file_offset, unwindstack:
}
#endif
UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {}
bool UnwindStackOfflineMap::Build() {
return false;
}
bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps,
const backtrace_stackinfo_t& stack) {
if (stack.start >= stack.end) {
return false;
}
for (const backtrace_map_t& map : backtrace_maps) {
maps_.push_back(map);
}
std::sort(maps_.begin(), maps_.end(),
[](const backtrace_map_t& a, const backtrace_map_t& b) { return a.start < b.start; });
unwindstack::Maps* maps = new unwindstack::Maps;
stack_maps_.reset(maps);
for (const backtrace_map_t& map : maps_) {
maps->Add(map.start, map.end, map.offset, map.flags, map.name, map.load_bias);
}
// Create the process memory from the stack data.
uint64_t size = stack.end - stack.start;
unwindstack::MemoryBuffer* memory = new unwindstack::MemoryBuffer;
memory->Resize(size);
memcpy(memory->GetPtr(0), stack.data, size);
std::shared_ptr<unwindstack::Memory> shared_memory(memory);
process_memory_.reset(new unwindstack::MemoryRange(shared_memory, 0, size, stack.start));
return true;
}
//-------------------------------------------------------------------------
// BacktraceMap create function.
//-------------------------------------------------------------------------
@ -167,3 +204,16 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
}
return map;
}
//-------------------------------------------------------------------------
// BacktraceMap create offline function.
//-------------------------------------------------------------------------
BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
const backtrace_stackinfo_t& stack) {
UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
if (!map->Build(maps, stack)) {
delete map;
return nullptr;
}
return map;
}

View file

@ -23,7 +23,9 @@
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@ -63,4 +65,14 @@ class UnwindStackMap : public BacktraceMap {
#endif
};
class UnwindStackOfflineMap : public UnwindStackMap {
public:
UnwindStackOfflineMap(pid_t pid);
~UnwindStackOfflineMap() = default;
bool Build() override;
bool Build(const std::vector<backtrace_map_t>& maps, const backtrace_stackinfo_t& stack);
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H

View file

@ -15,9 +15,9 @@
*/
#include <inttypes.h>
#include <libunwind.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <functional>
@ -44,33 +44,7 @@ int test_level_two(int, int, int, int, void (*)(void*), void*);
int test_level_three(int, int, int, int, void (*)(void*), void*);
int test_level_four(int, int, int, int, void (*)(void*), void*);
int test_recursive_call(int, void (*)(void*), void*);
void test_get_context_and_wait(unw_context_t* unw_context, volatile int* exit_flag);
}
static ucontext_t GetUContextFromUnwContext(const unw_context_t& unw_context) {
ucontext_t ucontext;
memset(&ucontext, 0, sizeof(ucontext));
#if defined(__arm__)
ucontext.uc_mcontext.arm_r0 = unw_context.regs[0];
ucontext.uc_mcontext.arm_r1 = unw_context.regs[1];
ucontext.uc_mcontext.arm_r2 = unw_context.regs[2];
ucontext.uc_mcontext.arm_r3 = unw_context.regs[3];
ucontext.uc_mcontext.arm_r4 = unw_context.regs[4];
ucontext.uc_mcontext.arm_r5 = unw_context.regs[5];
ucontext.uc_mcontext.arm_r6 = unw_context.regs[6];
ucontext.uc_mcontext.arm_r7 = unw_context.regs[7];
ucontext.uc_mcontext.arm_r8 = unw_context.regs[8];
ucontext.uc_mcontext.arm_r9 = unw_context.regs[9];
ucontext.uc_mcontext.arm_r10 = unw_context.regs[10];
ucontext.uc_mcontext.arm_fp = unw_context.regs[11];
ucontext.uc_mcontext.arm_ip = unw_context.regs[12];
ucontext.uc_mcontext.arm_sp = unw_context.regs[13];
ucontext.uc_mcontext.arm_lr = unw_context.regs[14];
ucontext.uc_mcontext.arm_pc = unw_context.regs[15];
#else
ucontext.uc_mcontext = unw_context.uc_mcontext;
#endif
return ucontext;
void test_get_context_and_wait(void* context, volatile int* exit_flag);
}
struct FunctionSymbol {
@ -108,18 +82,17 @@ static std::string RawDataToHexString(const void* data, size_t size) {
return s;
}
static void HexStringToRawData(const char* s, void* data, size_t size) {
uint8_t* p = static_cast<uint8_t*>(data);
static void HexStringToRawData(const char* s, std::vector<uint8_t>* data, size_t size) {
for (size_t i = 0; i < size; ++i) {
int value;
sscanf(s, "%02x", &value);
*p++ = static_cast<uint8_t>(value);
data->push_back(value);
s += 2;
}
}
struct OfflineThreadArg {
unw_context_t unw_context;
std::vector<uint8_t> ucontext;
pid_t tid;
volatile int exit_flag;
};
@ -127,12 +100,12 @@ struct OfflineThreadArg {
static void* OfflineThreadFunc(void* arg) {
OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
fn_arg->tid = gettid();
test_get_context_and_wait(&fn_arg->unw_context, &fn_arg->exit_flag);
test_get_context_and_wait(&fn_arg->ucontext, &fn_arg->exit_flag);
return nullptr;
}
std::string GetTestPath(std::string path) {
return android::base::GetExecutableDirectory() + "/testdata/" + ABI_STRING + '/' + path;
std::string GetTestPath(const std::string& arch, const std::string& path) {
return android::base::GetExecutableDirectory() + "/testdata/" + arch + '/' + path;
}
// This test is disable because it is for generating test data.
@ -149,7 +122,7 @@ TEST(libbacktrace, DISABLED_generate_offline_testdata) {
OfflineThreadArg arg;
arg.exit_flag = 0;
ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
// Wait for the offline thread to generate the stack and unw_context information.
// Wait for the offline thread to generate the stack and context information.
sleep(1);
// Copy the stack information.
std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
@ -179,9 +152,9 @@ TEST(libbacktrace, DISABLED_generate_offline_testdata) {
entry->start, entry->end, entry->offset, entry->load_bias,
entry->flags, entry->name.c_str());
}
// 3. Dump registers
testdata += android::base::StringPrintf("registers: %zu ", sizeof(arg.unw_context));
testdata += RawDataToHexString(&arg.unw_context, sizeof(arg.unw_context));
// 3. Dump ucontext
testdata += android::base::StringPrintf("ucontext: %zu ", arg.ucontext.size());
testdata += RawDataToHexString(arg.ucontext.data(), arg.ucontext.size());
testdata.push_back('\n');
// 4. Dump stack
@ -218,7 +191,7 @@ struct OfflineTestData {
int pid;
int tid;
std::vector<backtrace_map_t> maps;
unw_context_t unw_context;
std::vector<uint8_t> ucontext;
backtrace_stackinfo_t stack_info;
std::vector<uint8_t> stack;
std::vector<FunctionSymbol> symbols;
@ -231,7 +204,6 @@ bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestDat
}
// Parse offline_testdata.
std::vector<std::string> lines = android::base::Split(s, "\n");
memset(&testdata->unw_context, 0, sizeof(testdata->unw_context));
for (const auto& line : lines) {
if (android::base::StartsWith(line, "pid:")) {
sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
@ -244,50 +216,12 @@ bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestDat
" flags: %d name: %n",
&map.start, &map.end, &map.offset, &map.load_bias, &map.flags, &pos);
map.name = android::base::Trim(line.substr(pos));
} else if (android::base::StartsWith(line, "registers:")) {
} else if (android::base::StartsWith(line, "ucontext:")) {
size_t size;
int pos;
sscanf(line.c_str(), "registers: %zu %n", &size, &pos);
if (sizeof(testdata->unw_context) != size) {
return false;
}
HexStringToRawData(&line[pos], &testdata->unw_context, size);
} else if (android::base::StartsWith(line, "regs:")) {
std::vector<std::string> strs = android::base::Split(line.substr(6), " ");
if (strs.size() % 2 != 0) {
return false;
}
std::vector<std::pair<std::string, uint64_t>> items;
for (size_t i = 0; i + 1 < strs.size(); i += 2) {
if (!android::base::EndsWith(strs[i], ":")) {
return false;
}
uint64_t value = std::stoul(strs[i + 1], nullptr, 16);
items.push_back(std::make_pair(strs[i].substr(0, strs[i].size() - 1), value));
}
#if defined(__arm__)
for (auto& item : items) {
if (item.first == "sp") {
testdata->unw_context.regs[13] = item.second;
} else if (item.first == "pc") {
testdata->unw_context.regs[15] = item.second;
} else {
return false;
}
}
#elif defined(__aarch64__)
for (auto& item : items) {
if (item.first == "pc") {
testdata->unw_context.uc_mcontext.pc = item.second;
} else if (item.first == "sp") {
testdata->unw_context.uc_mcontext.sp = item.second;
} else if (item.first == "x29") {
testdata->unw_context.uc_mcontext.regs[UNW_AARCH64_X29] = item.second;
} else {
return false;
}
}
#endif
testdata->ucontext.clear();
sscanf(line.c_str(), "ucontext: %zu %n", &size, &pos);
HexStringToRawData(&line[pos], &testdata->ucontext, size);
} else if (android::base::StartsWith(line, "stack:")) {
size_t size;
int pos;
@ -295,8 +229,8 @@ bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestDat
"stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
&testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
CHECK_EQ(testdata->stack_info.end - testdata->stack_info.start, size);
testdata->stack.resize(size);
HexStringToRawData(&line[pos], &testdata->stack[0], size);
testdata->stack.clear();
HexStringToRawData(&line[pos], &testdata->stack, size);
testdata->stack_info.data = testdata->stack.data();
} else if (android::base::StartsWith(line, "function:")) {
testdata->symbols.resize(testdata->symbols.size() + 1);
@ -310,17 +244,11 @@ bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestDat
return true;
}
static void BacktraceOfflineTest(const char* arch, const std::string& testlib_name) {
// TODO: For now, we can only run this on the same arch as the library arch.
if (std::string(ABI_STRING) != arch) {
GTEST_LOG_(INFO) << "Ignoring arch " << arch << " for lib " << testlib_name;
return;
}
const std::string testlib_path(GetTestPath(testlib_name));
const std::string offline_testdata_path(GetTestPath("offline_testdata"));
static void BacktraceOfflineTest(std::string arch_str, const std::string& testlib_name) {
const std::string testlib_path(GetTestPath(arch_str, testlib_name));
const std::string offline_testdata_path(GetTestPath(arch_str, "offline_testdata"));
OfflineTestData testdata;
ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata)) << "Failed " << arch_str;
// Fix path of libbacktrace_testlib.so.
for (auto& map : testdata.maps) {
@ -329,16 +257,24 @@ static void BacktraceOfflineTest(const char* arch, const std::string& testlib_na
}
}
// Do offline backtrace.
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
ASSERT_TRUE(map != nullptr);
Backtrace::ArchEnum arch;
if (arch_str == "arm") {
arch = Backtrace::ARCH_ARM;
} else if (arch_str == "arm64") {
arch = Backtrace::ARCH_ARM64;
} else if (arch_str == "x86") {
arch = Backtrace::ARCH_X86;
} else if (arch_str == "x86_64") {
arch = Backtrace::ARCH_X86_64;
} else {
abort();
}
std::unique_ptr<Backtrace> backtrace(
Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
ASSERT_TRUE(backtrace != nullptr);
std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
ASSERT_TRUE(backtrace != nullptr) << "Failed " << arch_str;
ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data())) << "Failed " << arch_str;
// Collect pc values of the call stack frames.
std::vector<uint64_t> pc_values;
@ -354,14 +290,17 @@ static void BacktraceOfflineTest(const char* arch, const std::string& testlib_na
}
}
ASSERT_GE(test_one_index, 3u);
ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols));
ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1],
testdata.symbols));
ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2],
testdata.symbols));
ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3],
testdata.symbols));
ASSERT_GE(test_one_index, 3u) << "Failed " << arch_str;
ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols))
<< "Failed " << arch_str;
ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], testdata.symbols))
<< "Failed " << arch_str;
ASSERT_EQ("test_level_three",
FunctionNameForAddress(pc_values[test_one_index - 2], testdata.symbols))
<< "Failed " << arch_str;
ASSERT_EQ("test_level_four",
FunctionNameForAddress(pc_values[test_one_index - 3], testdata.symbols))
<< "Failed " << arch_str;
}
// For now, these tests can only run on the given architectures.
@ -384,18 +323,13 @@ TEST(libbacktrace, offline_arm_exidx) {
BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
}
static void LibUnwindingTest(const std::string& arch, const std::string& testdata_name,
static void LibUnwindingTest(const std::string& arch_str, const std::string& testdata_name,
const std::string& testlib_name) {
if (std::string(ABI_STRING) != arch) {
GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
<< " isn't supported.";
return;
}
const std::string testlib_path(GetTestPath(testlib_name));
const std::string testlib_path(GetTestPath(arch_str, testlib_name));
struct stat st;
ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
const std::string offline_testdata_path(GetTestPath(testdata_name));
const std::string offline_testdata_path(GetTestPath(arch_str, testdata_name));
OfflineTestData testdata;
ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
@ -406,26 +340,35 @@ static void LibUnwindingTest(const std::string& arch, const std::string& testdat
}
}
// Do offline backtrace.
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
ASSERT_TRUE(map != nullptr);
Backtrace::ArchEnum arch;
if (arch_str == "arm") {
arch = Backtrace::ARCH_ARM;
} else if (arch_str == "arm64") {
arch = Backtrace::ARCH_ARM64;
} else if (arch_str == "x86") {
arch = Backtrace::ARCH_X86;
} else if (arch_str == "x86_64") {
arch = Backtrace::ARCH_X86_64;
} else {
ASSERT_TRUE(false) << "Unsupported arch " << arch_str;
abort();
}
std::unique_ptr<Backtrace> backtrace(
Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
// Do offline backtrace.
std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
ASSERT_TRUE(backtrace != nullptr);
ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data()));
ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
uint64_t vaddr_in_file =
backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
std::string name = FunctionNameForAddress(backtrace->GetFrame(i)->rel_pc, testdata.symbols);
ASSERT_EQ(name, testdata.symbols[i].name);
}
ASSERT_TRUE(backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_ACCESS_MEM_FAILED ||
backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING);
backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING ||
backtrace->GetError().error_code == BACKTRACE_UNWIND_ERROR_REPEATED_FRAME);
}
// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx

View file

@ -31,6 +31,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <ucontext.h>
#include <unistd.h>
#include <algorithm>
@ -143,6 +144,17 @@ static void FinishRemoteProcess(pid_t pid) {
ASSERT_EQ(waitpid(pid, nullptr, 0), pid);
}
#if !defined(__ANDROID__) || defined(__arm__)
// On host and arm target we aren't guaranteed that we will terminate cleanly.
#define VERIFY_NO_ERROR(error_code) \
ASSERT_TRUE(error_code == BACKTRACE_UNWIND_NO_ERROR || \
error_code == BACKTRACE_UNWIND_ERROR_UNWIND_INFO || \
error_code == BACKTRACE_UNWIND_ERROR_MAP_MISSING) \
<< "Unknown error code " << std::to_string(error_code);
#else
#define VERIFY_NO_ERROR(error_code) ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, error_code);
#endif
static bool ReadyLevelBacktrace(Backtrace* backtrace) {
// See if test_level_four is in the backtrace.
bool found = false;
@ -189,7 +201,7 @@ static void VerifyLevelBacktrace(void*) {
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
VerifyLevelDump(backtrace.get());
}
@ -211,7 +223,7 @@ static void VerifyMaxBacktrace(void*) {
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
VerifyMaxDump(backtrace.get());
}
@ -241,7 +253,7 @@ TEST(libbacktrace, local_no_unwind_frames) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
ASSERT_TRUE(backtrace->NumFrames() != 0);
for (const auto& frame : *backtrace ) {
@ -292,19 +304,19 @@ static void VerifyLevelIgnoreFrames(void*) {
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError().error_code);
VERIFY_NO_ERROR(all->GetError().error_code);
std::unique_ptr<Backtrace> ign1(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
VERIFY_NO_ERROR(ign1->GetError().error_code);
std::unique_ptr<Backtrace> ign2(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
VERIFY_NO_ERROR(ign2->GetError().error_code);
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
}
@ -340,7 +352,6 @@ static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
if (ReadyFunc(backtrace.get())) {
VerifyFunc(backtrace.get(), create_func, map_create_func);
verified = true;
@ -389,12 +400,12 @@ static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_fu
std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
VERIFY_NO_ERROR(ign1->GetError().error_code);
std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
VERIFY_NO_ERROR(ign2->GetError().error_code);
VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
}
@ -480,7 +491,7 @@ void VerifyLevelThread(void*) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
VerifyLevelDump(backtrace.get());
}
@ -493,7 +504,7 @@ static void VerifyMaxThread(void*) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
VerifyMaxDump(backtrace.get());
}
@ -535,7 +546,7 @@ TEST(libbacktrace, thread_level_trace) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
VerifyLevelDump(backtrace.get());
@ -575,17 +586,17 @@ TEST(libbacktrace, thread_ignore_frames) {
std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError().error_code);
VERIFY_NO_ERROR(all->GetError().error_code);
std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError().error_code);
VERIFY_NO_ERROR(ign1->GetError().error_code);
std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError().error_code);
VERIFY_NO_ERROR(ign2->GetError().error_code);
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
@ -616,7 +627,7 @@ TEST(libbacktrace, thread_max_trace) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
ASSERT_EQ(BACKTRACE_UNWIND_ERROR_EXCEED_MAX_FRAMES_LIMIT, backtrace->GetError().error_code);
VerifyMaxDump(backtrace.get());
@ -713,21 +724,21 @@ TEST(libbacktrace, simultaneous_maps) {
Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
ASSERT_TRUE(back1 != nullptr);
EXPECT_TRUE(back1->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError().error_code);
VERIFY_NO_ERROR(back1->GetError().error_code);
delete back1;
delete map1;
Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
ASSERT_TRUE(back2 != nullptr);
EXPECT_TRUE(back2->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError().error_code);
VERIFY_NO_ERROR(back2->GetError().error_code);
delete back2;
delete map2;
Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
ASSERT_TRUE(back3 != nullptr);
EXPECT_TRUE(back3->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError().error_code);
VERIFY_NO_ERROR(back3->GetError().error_code);
delete back3;
delete map3;
}
@ -991,7 +1002,7 @@ static void RunReadTest(Backtrace* backtrace, uint64_t read_addr) {
uint8_t* expected = new uint8_t[pagesize];
InitMemory(expected, pagesize);
uint8_t* data = new uint8_t[2*pagesize];
uint8_t* data = new uint8_t[2 * pagesize];
// Verify that we can only read one page worth of data.
size_t bytes_read = backtrace->Read(read_addr, data, 2 * pagesize);
ASSERT_EQ(pagesize, bytes_read);
@ -1042,8 +1053,10 @@ TEST(libbacktrace, thread_read) {
ASSERT_TRUE(WaitForNonZero(&thread_data.state, 10));
}
// The code requires these variables are the same size.
volatile uint64_t g_ready = 0;
volatile uint64_t g_addr = 0;
static_assert(sizeof(g_ready) == sizeof(g_addr), "g_ready/g_addr must be same size");
static void ForkedReadTest() {
// Create two map pages.
@ -1091,13 +1104,13 @@ TEST(libbacktrace, process_read) {
uint64_t read_addr;
size_t bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(&g_ready),
reinterpret_cast<uint8_t*>(&read_addr), sizeof(uint64_t));
ASSERT_EQ(sizeof(uint64_t), bytes_read);
reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_ready));
ASSERT_EQ(sizeof(g_ready), bytes_read);
if (read_addr) {
// The forked process is ready to be read.
bytes_read = backtrace->Read(reinterpret_cast<uint64_t>(&g_addr),
reinterpret_cast<uint8_t*>(&read_addr), sizeof(uint64_t));
ASSERT_EQ(sizeof(uint64_t), bytes_read);
reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_addr));
ASSERT_EQ(sizeof(g_addr), bytes_read);
RunReadTest(backtrace.get(), read_addr);
@ -1176,7 +1189,7 @@ TEST(libbacktrace, check_unreadable_elf_local) {
int fd = open(tmp_so_name, O_RDONLY);
ASSERT_TRUE(fd != -1);
void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(map != MAP_FAILED);
close(fd);
ASSERT_TRUE(unlink(tmp_so_name) != -1);
@ -1225,7 +1238,7 @@ TEST(libbacktrace, check_unreadable_elf_remote) {
exit(0);
}
void* map = mmap(NULL, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
void* map = mmap(nullptr, map_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
if (map == MAP_FAILED) {
fprintf(stderr, "Failed to map in memory: %s\n", strerror(errno));
unlink(tmp_so_name);
@ -1258,11 +1271,11 @@ TEST(libbacktrace, check_unreadable_elf_remote) {
ASSERT_TRUE(backtrace.get() != nullptr);
uint64_t read_addr;
ASSERT_EQ(sizeof(uint64_t),
ASSERT_EQ(sizeof(g_ready),
backtrace->Read(reinterpret_cast<uint64_t>(&g_ready),
reinterpret_cast<uint8_t*>(&read_addr), sizeof(uint64_t)));
reinterpret_cast<uint8_t*>(&read_addr), sizeof(g_ready)));
if (read_addr) {
ASSERT_EQ(sizeof(uint64_t),
ASSERT_EQ(sizeof(g_addr),
backtrace->Read(reinterpret_cast<uint64_t>(&g_addr),
reinterpret_cast<uint8_t*>(&read_addr), sizeof(uint64_t)));
@ -1328,15 +1341,16 @@ static void VerifyUnreadableElfFrame(Backtrace* backtrace, uint64_t test_func, s
}
static void VerifyUnreadableElfBacktrace(void* func) {
uint64_t test_func = reinterpret_cast<uint64_t>(func);
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS,
BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
size_t frame_num;
ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
uint64_t test_func = reinterpret_cast<uint64_t>(func);
ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num))
<< DumpFrames(backtrace.get());
VerifyUnreadableElfFrame(backtrace.get(), test_func, frame_num);
}
@ -1390,7 +1404,7 @@ TEST(libbacktrace, unwind_through_unreadable_elf_remote) {
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(), reinterpret_cast<uint64_t>(test_func),
@ -1786,7 +1800,7 @@ static void CheckForLeak(pid_t pid, pid_t tid) {
Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
delete backtrace;
}
size_t stable_pss = GetPssBytes();
@ -1797,7 +1811,7 @@ static void CheckForLeak(pid_t pid, pid_t tid) {
Backtrace* backtrace = Backtrace::Create(pid, tid, map.get());
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError().error_code);
VERIFY_NO_ERROR(backtrace->GetError().error_code);
delete backtrace;
}
size_t new_pss = GetPssBytes();

View file

@ -14,9 +14,15 @@
* limitations under the License.
*/
#include <libunwind.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <memory>
#include <vector>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
#include "backtrace_testlib.h"
@ -66,21 +72,70 @@ int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
}
typedef struct {
unw_context_t* unw_context;
std::vector<uint8_t>* ucontext;
volatile int* exit_flag;
} GetContextArg;
static void GetContextAndExit(void* data) {
GetContextArg* arg = (GetContextArg*)data;
unw_getcontext(arg->unw_context);
GetContextArg* arg = reinterpret_cast<GetContextArg*>(data);
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
unwindstack::RegsGetLocal(regs.get());
ucontext_t ucontext;
memset(&ucontext, 0, sizeof(ucontext));
#if defined(__arm__)
memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint32_t) * 16);
#elif defined(__aarch64__)
memcpy(&ucontext.uc_mcontext, regs->RawData(), sizeof(uint64_t) * 33);
#elif defined(__i386__)
uint32_t* reg_data = reinterpret_cast<uint32_t*>(regs->RawData());
ucontext.uc_mcontext.gregs[0] = reg_data[15];
ucontext.uc_mcontext.gregs[1] = reg_data[14];
ucontext.uc_mcontext.gregs[2] = reg_data[13];
ucontext.uc_mcontext.gregs[3] = reg_data[12];
ucontext.uc_mcontext.gregs[4] = reg_data[7];
ucontext.uc_mcontext.gregs[5] = reg_data[6];
ucontext.uc_mcontext.gregs[6] = reg_data[5];
ucontext.uc_mcontext.gregs[7] = reg_data[4];
ucontext.uc_mcontext.gregs[8] = reg_data[3];
ucontext.uc_mcontext.gregs[9] = reg_data[2];
ucontext.uc_mcontext.gregs[10] = reg_data[1];
ucontext.uc_mcontext.gregs[11] = reg_data[0];
ucontext.uc_mcontext.gregs[14] = reg_data[8];
ucontext.uc_mcontext.gregs[15] = reg_data[10];
#elif defined(__x86_64__)
uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
ucontext.uc_mcontext.gregs[0] = reg_data[8];
ucontext.uc_mcontext.gregs[1] = reg_data[9];
ucontext.uc_mcontext.gregs[2] = reg_data[10];
ucontext.uc_mcontext.gregs[3] = reg_data[11];
ucontext.uc_mcontext.gregs[4] = reg_data[12];
ucontext.uc_mcontext.gregs[5] = reg_data[13];
ucontext.uc_mcontext.gregs[6] = reg_data[14];
ucontext.uc_mcontext.gregs[7] = reg_data[15];
ucontext.uc_mcontext.gregs[8] = reg_data[5];
ucontext.uc_mcontext.gregs[9] = reg_data[4];
ucontext.uc_mcontext.gregs[10] = reg_data[6];
ucontext.uc_mcontext.gregs[11] = reg_data[3];
ucontext.uc_mcontext.gregs[12] = reg_data[1];
ucontext.uc_mcontext.gregs[13] = reg_data[0];
ucontext.uc_mcontext.gregs[14] = reg_data[2];
ucontext.uc_mcontext.gregs[15] = reg_data[7];
ucontext.uc_mcontext.gregs[16] = reg_data[16];
#endif
arg->ucontext->resize(sizeof(ucontext));
memcpy(arg->ucontext->data(), &ucontext, sizeof(ucontext));
// Don't touch the stack anymore.
while (*arg->exit_flag == 0) {
}
}
void test_get_context_and_wait(unw_context_t* unw_context, volatile int* exit_flag) {
void test_get_context_and_wait(void* ucontext, volatile int* exit_flag) {
GetContextArg arg;
arg.unw_context = unw_context;
arg.ucontext = reinterpret_cast<std::vector<uint8_t>*>(ucontext);
arg.exit_flag = exit_flag;
test_level_one(1, 2, 3, 4, GetContextAndExit, &arg);
}

View file

@ -19,8 +19,6 @@
#include <sys/cdefs.h>
#include <libunwind.h>
__BEGIN_DECLS
void test_loop_forever();
@ -31,7 +29,7 @@ int test_level_three(int, int, int, int, void (*)(void*), void*);
int test_level_two(int, int, int, int, void (*)(void*), void*);
int test_level_one(int, int, int, int, void (*)(void*), void*);
int test_recursive_call(int, void (*)(void*), void*);
void test_get_context_and_wait(unw_context_t*, volatile int*);
void test_get_context_and_wait(void*, volatile int*);
__END_DECLS

View file

@ -60,6 +60,10 @@ enum BacktraceUnwindErrorCode : uint32_t {
BACKTRACE_UNWIND_ERROR_FIND_PROC_INFO_FAILED,
// Failed to execute dwarf instructions in debug sections.
BACKTRACE_UNWIND_ERROR_EXECUTE_DWARF_INSTRUCTION_FAILED,
// Unwind information is incorrect.
BACKTRACE_UNWIND_ERROR_UNWIND_INFO,
// Unwind information stopped due to sp/pc repeating.
BACKTRACE_UNWIND_ERROR_REPEATED_FRAME,
};
struct BacktraceUnwindError {
@ -87,14 +91,6 @@ struct backtrace_frame_data_t {
// NULL.
};
#if defined(__APPLE__)
struct __darwin_ucontext;
typedef __darwin_ucontext ucontext_t;
#else
struct ucontext;
typedef ucontext ucontext_t;
#endif
struct backtrace_stackinfo_t {
uint64_t start;
uint64_t end;
@ -106,7 +102,16 @@ class Regs;
}
class Backtrace {
public:
public:
enum ArchEnum : uint8_t {
ARCH_ARM,
ARCH_ARM64,
ARCH_X86,
ARCH_X86_64,
};
static void SetGlobalElfCache(bool enable);
// Create the correct Backtrace object based on what is to be unwound.
// If pid < 0 or equals the current pid, then the Backtrace object
// corresponds to the current process.
@ -119,6 +124,16 @@ public:
// If map is not NULL, the map is still owned by the caller.
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
// Create an offline Backtrace object that can be used to do an unwind without a process
// that is still running. By default, information is only cached in the map
// file. If the calling code creates the map, data can be cached between
// unwinds. If not, all cached data will be destroyed when the Backtrace
// object is destroyed.
static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
const std::vector<backtrace_map_t>& maps,
const backtrace_stackinfo_t& stack);
static Backtrace* CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map);
// Create an offline Backtrace object that can be used to do an unwind without a process
// that is still running. If cache_file is set to true, then elf information will be cached
// for this call. The cached information survives until the calling process ends. This means
@ -130,11 +145,11 @@ public:
virtual ~Backtrace();
// Get the current stack trace and store in the backtrace_ structure.
virtual bool Unwind(size_t num_ignore_frames, ucontext_t* context = NULL) = 0;
virtual bool Unwind(size_t num_ignore_frames, void* context = NULL) = 0;
static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names);
std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
// Get the function name and offset into the function given the pc.
// If the string is empty, then no valid function name was found,
@ -184,7 +199,7 @@ public:
std::string GetErrorString(BacktraceUnwindError error);
protected:
protected:
Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
// The name returned is not demangled, GetFunctionName() takes care of

View file

@ -34,6 +34,9 @@
#include <string>
#include <vector>
// Forward declaration.
struct backtrace_stackinfo_t;
// Special flag to indicate a map is in /dev/. However, a map in
// /dev/ashmem/... does not set this flag.
static constexpr int PROT_DEVICE_MAP = 0x8000;
@ -58,7 +61,8 @@ public:
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
const backtrace_stackinfo_t& stack);
virtual ~BacktraceMap();

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
pid: 7288 tid: 31656
regs: pc: cc416235 sp: cc17f000
ucontext: 104 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f017cc00000000356241cc0000000000000000
map: start: cc361000 end: cc758000 offset: 0 load_bias: 9000 flags: 5 name: /vendor/lib/egl/libGLESv2_adreno.so
stack: start: cc17f254 end: cc17f258 size: 4 b36141cc
stack: start: cc17f234 end: cc17f258 size: 36 0000000000000000000000000000000000000000000000000000000000000000b36141cc
function: start: be1f0 end: be304 name: EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)
function: start: be058 end: be1f0 name: EsxContext::ClearBuffersForDebug()

View file

@ -1,6 +1,6 @@
pid: 7288 tid: 31656
regs: pc: f1f6dc49 sp: d8fe6930
ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003069fed80000000049dcf6f10000000000000000
map: start: f1f10000 end: f2049000 offset: 0 load_bias: 10000 flags: 5 name: /system/lib/libandroid_runtime.so
stack: start: d8fe6954 end: d8fe6958 size: 4 e7dcf6f1
stack: start: d8fe6948 end: d8fe6958 size: 16 000000000000000000000000e7dcf6f1
function: start: 6dbf9 end: 6dce5 name: android::AndroidRuntime::javaThreadShell
function: start: 6dce5 end: 6dd79 name: android::AndroidRuntime::javaCreateThreadEtc

View file

@ -1,5 +1,5 @@
pid: 32232 tid: 32233
registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
ucontext: 104 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e90000000000000000
map: start: e9380000 end: e9766000 offset: 0 load_bias: b000 flags: 5 name: /system/lib/libart.so
stack: start: ffd12dc0 end: ffd1306c size: 684 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d89524
function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
pid: 12276 tid: 12303
regs: pc: 7b8c027f64 sp: 7b8c157010 x29: 7b8c157040
map: start: 7b8c01e000 end: 7b8c030000 offset: 0 load_bias: 0 flags: 5 name: /vendor/lib64/egl/eglSubDriverAndroid.so
stack: start: 7b8c157048 end: 7b8c157050 size: 8 547e028c7b000000
ucontext: 464 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004070158c7b00000000000000000000001070158c7b000000647f028c7b00000000000000000000000000000000000000
stack: start: 7b8c157020 end: 7b8c157050 size: 48 00000000000000000000000000000000000000000000000000000000000000000000000000000000547e028c7b000000
function: start: 9ed8 end: a1b0 name: EglAndroidWindowSurface::Initialize(EglAndroidConfig*, int const*)
function: start: 9dcc end: 9ed8 name: EglAndroidWindowSurface::Create(ANativeWindow*, EglAndroidConfig*, EglAndroidWindowSurface**, int const*)

View file

@ -1,6 +1,6 @@
pid: 32232 tid: 32233
regs: pc: 7c25189a0c sp: 7b8c154b50 x29: 7b8c154bb0
map: start: 7c24c80000 end: 7c25413000 offset: 0 load_bias: 5f000 flags: 5 name: /system/lib64/libskia.so
stack: start: 7b8c154bb8 end: 7b8c154bc0 size: 8 ec43f2247c000000
ucontext: 464 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b04b158c7b0000000000000000000000504b158c7b0000000c9a18257c00000000000000000000000000000000000000
stack: start: 7b8c154b80 end: 7b8c154bc0 size: 64 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec43f2247c000000
function: start: 568970 end: 568c08 name: SkScalerContext_FreeType::generateImage(SkGlyph const&)
function: start: 30330c end: 3044b0 name: SkScalerContext::getImage(SkGlyph const&)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long