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:
parent
8abe4e2638
commit
c8bec5aa91
26 changed files with 424 additions and 1513 deletions
|
@ -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"],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
2
libbacktrace/testdata/arm/offline_testdata
vendored
2
libbacktrace/testdata/arm/offline_testdata
vendored
File diff suppressed because one or more lines are too long
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
2
libbacktrace/testdata/arm64/offline_testdata
vendored
2
libbacktrace/testdata/arm64/offline_testdata
vendored
File diff suppressed because one or more lines are too long
|
@ -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*)
|
||||
|
|
|
@ -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&)
|
||||
|
|
2
libbacktrace/testdata/x86/offline_testdata
vendored
2
libbacktrace/testdata/x86/offline_testdata
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue