Internalize subclasses of Memory
There are many subclasses of the Memory class and the overwhelming majority of them don't need to be exposed externally. We move all of them to internal headers except MemoryOfflineBuffer, which moves to a separate header. This dramatically reduces the exposed API surface and makes the code more modular. Also, remove the Offline code from libbacktrace. It's not used any where. Test: Unit tests pass, clean tree still builds Change-Id: I55dacdf080daba0bfe65c1ad53a4b326bb482e83
This commit is contained in:
parent
1d7f3b4f4e
commit
6b95a0e999
36 changed files with 453 additions and 790 deletions
|
@ -135,7 +135,6 @@ cc_test {
|
|||
defaults: ["libbacktrace_common"],
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"backtrace_offline_test.cpp",
|
||||
"backtrace_test.cpp",
|
||||
],
|
||||
|
||||
|
|
|
@ -129,22 +129,6 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
const backtrace_stackinfo_t& stack,
|
||||
std::vector<backtrace_frame_data_t>* frames,
|
||||
BacktraceUnwindError* error) {
|
||||
UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
|
||||
// Create the process memory from the stack data since this will almost
|
||||
// always be different each unwind.
|
||||
if (!offline_map->CreateProcessMemory(stack)) {
|
||||
if (error != nullptr) {
|
||||
error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
|
||||
}
|
||||
|
||||
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktraceCurrent(pid, tid, map) {}
|
||||
|
||||
|
@ -171,7 +155,7 @@ bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, void* ucont
|
|||
}
|
||||
|
||||
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktracePtrace(pid, tid, map), memory_(pid) {}
|
||||
: BacktracePtrace(pid, tid, map), memory_(unwindstack::Memory::CreateProcessMemory(pid)) {}
|
||||
|
||||
std::string UnwindStackPtrace::GetFunctionNameRaw(uint64_t pc, uint64_t* offset) {
|
||||
return GetMap()->GetFunctionName(pc, offset);
|
||||
|
@ -189,73 +173,5 @@ bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, void* context) {
|
|||
}
|
||||
|
||||
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) {
|
||||
std::unique_ptr<UnwindStackOfflineMap> map(
|
||||
reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
|
||||
if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
|
||||
return nullptr;
|
||||
}
|
||||
return new UnwindStackOffline(arch, pid, tid, map.release(), 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);
|
||||
return memory_->Read(addr, buffer, bytes);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
@ -49,23 +50,7 @@ class UnwindStackPtrace : public BacktracePtrace {
|
|||
size_t Read(uint64_t addr, uint8_t* buffer, size_t bytes) override;
|
||||
|
||||
private:
|
||||
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) override;
|
||||
|
||||
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_;
|
||||
std::shared_ptr<unwindstack::Memory> memory_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_STACK_H
|
||||
|
|
|
@ -132,43 +132,6 @@ std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
|
|||
return process_memory_;
|
||||
}
|
||||
|
||||
UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {}
|
||||
|
||||
bool UnwindStackOfflineMap::Build() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps) {
|
||||
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);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& stack) {
|
||||
if (stack.start >= stack.end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the process memory from the stack data.
|
||||
if (memory_ == nullptr) {
|
||||
memory_ = new unwindstack::MemoryOfflineBuffer(stack.data, stack.start, stack.end);
|
||||
process_memory_.reset(memory_);
|
||||
} else {
|
||||
memory_->Reset(stack.data, stack.start, stack.end);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// BacktraceMap create function.
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -189,15 +152,3 @@ 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) {
|
||||
UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
|
||||
if (!map->Build(maps)) {
|
||||
delete map;
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
// Forward declarations.
|
||||
class UnwindDexFile;
|
||||
|
@ -74,19 +75,4 @@ class UnwindStackMap : public BacktraceMap {
|
|||
unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
|
||||
};
|
||||
|
||||
class UnwindStackOfflineMap : public UnwindStackMap {
|
||||
public:
|
||||
UnwindStackOfflineMap(pid_t pid);
|
||||
~UnwindStackOfflineMap() = default;
|
||||
|
||||
bool Build() override;
|
||||
|
||||
bool Build(const std::vector<backtrace_map_t>& maps);
|
||||
|
||||
bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
|
||||
|
||||
private:
|
||||
unwindstack::MemoryOfflineBuffer* memory_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
|
|
|
@ -1,397 +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.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/threads.h>
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "BacktraceTest.h"
|
||||
|
||||
struct FunctionSymbol {
|
||||
std::string name;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
};
|
||||
|
||||
static std::vector<FunctionSymbol> GetFunctionSymbols() {
|
||||
std::vector<FunctionSymbol> symbols = {
|
||||
{"unknown_start", 0, 0},
|
||||
{"test_level_one", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_one_), 0},
|
||||
{"test_level_two", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_two_), 0},
|
||||
{"test_level_three", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_three_), 0},
|
||||
{"test_level_four", reinterpret_cast<uint64_t>(&BacktraceTest::test_level_four_), 0},
|
||||
{"test_recursive_call", reinterpret_cast<uint64_t>(&BacktraceTest::test_recursive_call_), 0},
|
||||
{"test_get_context_and_wait",
|
||||
reinterpret_cast<uint64_t>(&BacktraceTest::test_get_context_and_wait_), 0},
|
||||
{"unknown_end", static_cast<uint64_t>(-1), static_cast<uint64_t>(-1)},
|
||||
};
|
||||
std::sort(
|
||||
symbols.begin(), symbols.end(),
|
||||
[](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
|
||||
for (size_t i = 0; i + 1 < symbols.size(); ++i) {
|
||||
symbols[i].end = symbols[i + 1].start;
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
static std::string RawDataToHexString(const void* data, size_t size) {
|
||||
const uint8_t* p = static_cast<const uint8_t*>(data);
|
||||
std::string s;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
s += android::base::StringPrintf("%02x", p[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
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);
|
||||
data->push_back(value);
|
||||
s += 2;
|
||||
}
|
||||
}
|
||||
|
||||
struct OfflineThreadArg {
|
||||
std::vector<uint8_t> ucontext;
|
||||
pid_t tid;
|
||||
volatile int exit_flag;
|
||||
};
|
||||
|
||||
static void* OfflineThreadFunc(void* arg) {
|
||||
OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
|
||||
fn_arg->tid = android::base::GetThreadId();
|
||||
BacktraceTest::test_get_context_and_wait_(&fn_arg->ucontext, &fn_arg->exit_flag);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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.
|
||||
TEST_F(BacktraceTest, DISABLED_generate_offline_testdata) {
|
||||
// Create a thread to generate the needed stack and registers information.
|
||||
const size_t stack_size = 16 * 1024;
|
||||
void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
ASSERT_NE(MAP_FAILED, stack);
|
||||
uint64_t stack_addr = reinterpret_cast<uint64_t>(stack);
|
||||
pthread_attr_t attr;
|
||||
ASSERT_EQ(0, pthread_attr_init(&attr));
|
||||
ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
|
||||
pthread_t thread;
|
||||
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 context information.
|
||||
sleep(1);
|
||||
// Copy the stack information.
|
||||
std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
|
||||
reinterpret_cast<uint8_t*>(stack) + stack_size);
|
||||
arg.exit_flag = 1;
|
||||
ASSERT_EQ(0, pthread_join(thread, nullptr));
|
||||
ASSERT_EQ(0, munmap(stack, stack_size));
|
||||
|
||||
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
|
||||
ASSERT_TRUE(map != nullptr);
|
||||
|
||||
backtrace_stackinfo_t stack_info;
|
||||
stack_info.start = stack_addr;
|
||||
stack_info.end = stack_addr + stack_size;
|
||||
stack_info.data = stack_data.data();
|
||||
|
||||
// Generate offline testdata.
|
||||
std::string testdata;
|
||||
// 1. Dump pid, tid
|
||||
testdata += android::base::StringPrintf("pid: %d tid: %d\n", getpid(), arg.tid);
|
||||
// 2. Dump maps
|
||||
for (auto it = map->begin(); it != map->end(); ++it) {
|
||||
const backtrace_map_t* entry = *it;
|
||||
testdata +=
|
||||
android::base::StringPrintf("map: start: %" PRIx64 " end: %" PRIx64 " offset: %" PRIx64
|
||||
" load_bias: %" PRIx64 " flags: %d name: %s\n",
|
||||
entry->start, entry->end, entry->offset, entry->load_bias,
|
||||
entry->flags, entry->name.c_str());
|
||||
}
|
||||
// 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
|
||||
testdata += android::base::StringPrintf(
|
||||
"stack: start: %" PRIx64 " end: %" PRIx64 " size: %zu ",
|
||||
stack_info.start, stack_info.end, stack_data.size());
|
||||
testdata += RawDataToHexString(stack_data.data(), stack_data.size());
|
||||
testdata.push_back('\n');
|
||||
|
||||
// 5. Dump function symbols
|
||||
std::vector<FunctionSymbol> function_symbols = GetFunctionSymbols();
|
||||
for (const auto& symbol : function_symbols) {
|
||||
testdata +=
|
||||
android::base::StringPrintf("function: start: %" PRIx64 " end: %" PRIx64 " name: %s\n",
|
||||
symbol.start, symbol.end, symbol.name.c_str());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(android::base::WriteStringToFile(testdata, "offline_testdata"));
|
||||
}
|
||||
|
||||
// Return the name of the function which matches the address. Although we don't know the
|
||||
// exact end of each function, it is accurate enough for the tests.
|
||||
static std::string FunctionNameForAddress(uint64_t addr,
|
||||
const std::vector<FunctionSymbol>& symbols) {
|
||||
for (auto& symbol : symbols) {
|
||||
if (addr >= symbol.start && addr < symbol.end) {
|
||||
return symbol.name;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
struct OfflineTestData {
|
||||
int pid;
|
||||
int tid;
|
||||
std::vector<backtrace_map_t> maps;
|
||||
std::vector<uint8_t> ucontext;
|
||||
backtrace_stackinfo_t stack_info;
|
||||
std::vector<uint8_t> stack;
|
||||
std::vector<FunctionSymbol> symbols;
|
||||
};
|
||||
|
||||
bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestData* testdata) {
|
||||
std::string s;
|
||||
if (!android::base::ReadFileToString(offline_testdata_path, &s)) {
|
||||
return false;
|
||||
}
|
||||
// Parse offline_testdata.
|
||||
std::vector<std::string> lines = android::base::Split(s, "\n");
|
||||
for (const auto& line : lines) {
|
||||
if (android::base::StartsWith(line, "pid:")) {
|
||||
sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
|
||||
} else if (android::base::StartsWith(line, "map:")) {
|
||||
testdata->maps.resize(testdata->maps.size() + 1);
|
||||
backtrace_map_t& map = testdata->maps.back();
|
||||
int pos;
|
||||
sscanf(line.c_str(),
|
||||
"map: start: %" SCNx64 " end: %" SCNx64 " offset: %" SCNx64 " load_bias: %" SCNx64
|
||||
" 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, "ucontext:")) {
|
||||
size_t size;
|
||||
int pos;
|
||||
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;
|
||||
sscanf(line.c_str(),
|
||||
"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.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);
|
||||
FunctionSymbol& symbol = testdata->symbols.back();
|
||||
int pos;
|
||||
sscanf(line.c_str(), "function: start: %" SCNx64 " end: %" SCNx64 " name: %n", &symbol.start,
|
||||
&symbol.end, &pos);
|
||||
symbol.name = line.substr(pos);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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)) << "Failed " << arch_str;
|
||||
|
||||
// Fix path of libbacktrace_testlib.so.
|
||||
for (auto& map : testdata.maps) {
|
||||
if (map.name.find("libbacktrace_test.so") != std::string::npos) {
|
||||
map.name = testlib_path;
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
|
||||
ASSERT_TRUE(backtrace != nullptr) << "Failed " << arch_str;
|
||||
|
||||
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;
|
||||
for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
|
||||
pc_values.push_back(backtrace->GetFrame(i)->pc);
|
||||
}
|
||||
|
||||
size_t test_one_index = 0;
|
||||
for (size_t i = 0; i < pc_values.size(); ++i) {
|
||||
if (FunctionNameForAddress(pc_values[i], testdata.symbols) == "test_level_one") {
|
||||
test_one_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
TEST_F(BacktraceTest, offline_eh_frame) {
|
||||
BacktraceOfflineTest("arm64", "libbacktrace_test_eh_frame.so");
|
||||
BacktraceOfflineTest("x86_64", "libbacktrace_test_eh_frame.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_debug_frame) {
|
||||
BacktraceOfflineTest("arm", "libbacktrace_test_debug_frame.so");
|
||||
BacktraceOfflineTest("x86", "libbacktrace_test_debug_frame.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_gnu_debugdata) {
|
||||
BacktraceOfflineTest("arm", "libbacktrace_test_gnu_debugdata.so");
|
||||
BacktraceOfflineTest("x86", "libbacktrace_test_gnu_debugdata.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_arm_exidx) {
|
||||
BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
|
||||
}
|
||||
|
||||
static void LibUnwindingTest(const std::string& arch_str, const std::string& testdata_name,
|
||||
const std::string& 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(arch_str, testdata_name));
|
||||
OfflineTestData testdata;
|
||||
ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
|
||||
|
||||
// Fix path of the testlib.
|
||||
for (auto& map : testdata.maps) {
|
||||
if (map.name.find(testlib_name) != std::string::npos) {
|
||||
map.name = testlib_path;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// Do offline backtrace.
|
||||
std::unique_ptr<Backtrace> backtrace(Backtrace::CreateOffline(
|
||||
arch, testdata.pid, testdata.tid, testdata.maps, testdata.stack_info));
|
||||
ASSERT_TRUE(backtrace != nullptr);
|
||||
|
||||
ASSERT_TRUE(backtrace->Unwind(0, testdata.ucontext.data()));
|
||||
|
||||
ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
|
||||
for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
|
||||
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_REPEATED_FRAME);
|
||||
}
|
||||
|
||||
// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
|
||||
// overlap with each other, which appears in /system/lib/libart.so.
|
||||
TEST_F(BacktraceTest, offline_unwind_mix_eh_frame_and_arm_exidx) {
|
||||
LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_debug_frame_with_load_bias) {
|
||||
LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_try_armexidx_after_debug_frame) {
|
||||
LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_cie_with_P_augmentation) {
|
||||
// Make sure we can unwind through functions with CIE entry containing P augmentation, which
|
||||
// makes unwinding library reading personality handler from memory. One example is
|
||||
// /system/lib64/libskia.so.
|
||||
LibUnwindingTest("arm64", "offline_testdata_for_libskia", "libskia.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_empty_eh_frame_hdr) {
|
||||
// Make sure we can unwind through libraries with empty .eh_frame_hdr section. One example is
|
||||
// /vendor/lib64/egl/eglSubDriverAndroid.so.
|
||||
LibUnwindingTest("arm64", "offline_testdata_for_eglSubDriverAndroid", "eglSubDriverAndroid.so");
|
||||
}
|
||||
|
||||
TEST_F(BacktraceTest, offline_max_frames_limit) {
|
||||
// The length of callchain can reach 256 when recording an application.
|
||||
ASSERT_GE(MAX_BACKTRACE_FRAMES, 256);
|
||||
}
|
|
@ -126,24 +126,6 @@ class Backtrace {
|
|||
// If map is not NULL, the map is still owned by the caller.
|
||||
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = nullptr);
|
||||
|
||||
// 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
|
||||
// that subsequent calls to create offline Backtrace objects will continue to use the same
|
||||
// cache. It also assumes that the elf files used for each offline unwind are the same.
|
||||
static Backtrace* CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
|
||||
const backtrace_stackinfo_t& stack, bool cache_file = false);
|
||||
|
||||
virtual ~Backtrace();
|
||||
|
||||
// Get the current stack trace and store in the backtrace_ structure.
|
||||
|
@ -153,11 +135,6 @@ class Backtrace {
|
|||
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
|
||||
std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
|
||||
|
||||
static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
const backtrace_stackinfo_t& stack_info,
|
||||
std::vector<backtrace_frame_data_t>* frames,
|
||||
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,
|
||||
// or the pc is not in any valid map.
|
||||
|
|
|
@ -69,8 +69,6 @@ public:
|
|||
// is unsupported.
|
||||
static BacktraceMap* Create(pid_t pid, bool uncached = false);
|
||||
|
||||
static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
|
||||
|
||||
virtual ~BacktraceMap();
|
||||
|
||||
class iterator : public std::iterator<std::bidirectional_iterator_tag, backtrace_map_t*> {
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
#include <unwindstack/DwarfSection.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
#include "DwarfDebugFrame.h"
|
||||
#include "DwarfEhFrame.h"
|
||||
#include "DwarfEhFrameWithHdr.h"
|
||||
#include "MemoryBuffer.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryRange.h"
|
||||
|
||||
// This implements the JIT Compilation Interface.
|
||||
// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryFileAtOffset.h"
|
||||
#include "MemoryRange.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -32,6 +32,14 @@
|
|||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "Check.h"
|
||||
#include "MemoryBuffer.h"
|
||||
#include "MemoryCache.h"
|
||||
#include "MemoryFileAtOffset.h"
|
||||
#include "MemoryLocal.h"
|
||||
#include "MemoryOffline.h"
|
||||
#include "MemoryOfflineBuffer.h"
|
||||
#include "MemoryRange.h"
|
||||
#include "MemoryRemote.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
@ -168,6 +176,16 @@ bool Memory::ReadString(uint64_t addr, std::string* string, uint64_t max_read) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Memory> Memory::CreateFileMemory(const std::string& path, uint64_t offset) {
|
||||
auto memory = std::make_unique<MemoryFileAtOffset>();
|
||||
|
||||
if (memory->Init(path, offset)) {
|
||||
return memory;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
|
||||
if (pid == getpid()) {
|
||||
return std::shared_ptr<Memory>(new MemoryLocal());
|
||||
|
@ -182,6 +200,11 @@ std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
|
|||
return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory> Memory::CreateOfflineMemory(const uint8_t* data, uint64_t start,
|
||||
uint64_t end) {
|
||||
return std::shared_ptr<Memory>(new MemoryOfflineBuffer(data, start, end));
|
||||
}
|
||||
|
||||
size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (addr >= raw_.size()) {
|
||||
return 0;
|
||||
|
|
48
libunwindstack/MemoryBuffer.h
Normal file
48
libunwindstack/MemoryBuffer.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_BUFFER_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryBuffer : public Memory {
|
||||
public:
|
||||
MemoryBuffer() = default;
|
||||
virtual ~MemoryBuffer() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
uint8_t* GetPtr(size_t offset);
|
||||
|
||||
void Resize(size_t size) { raw_.resize(size); }
|
||||
|
||||
uint64_t Size() { return raw_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> raw_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_BUFFER_H
|
50
libunwindstack/MemoryCache.h
Normal file
50
libunwindstack/MemoryCache.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_CACHE_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_CACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryCache : public Memory {
|
||||
public:
|
||||
MemoryCache(Memory* memory) : impl_(memory) {}
|
||||
virtual ~MemoryCache() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
void Clear() override { cache_.clear(); }
|
||||
|
||||
private:
|
||||
constexpr static size_t kCacheBits = 12;
|
||||
constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
|
||||
constexpr static size_t kCacheSize = 1 << kCacheBits;
|
||||
std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
|
||||
|
||||
std::unique_ptr<Memory> impl_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_CACHE_H
|
47
libunwindstack/MemoryFileAtOffset.h
Normal file
47
libunwindstack/MemoryFileAtOffset.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryFileAtOffset : public Memory {
|
||||
public:
|
||||
MemoryFileAtOffset() = default;
|
||||
virtual ~MemoryFileAtOffset();
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
size_t Size() { return size_; }
|
||||
|
||||
void Clear() override;
|
||||
|
||||
protected:
|
||||
size_t size_ = 0;
|
||||
size_t offset_ = 0;
|
||||
uint8_t* data_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
|
36
libunwindstack/MemoryLocal.h
Normal file
36
libunwindstack/MemoryLocal.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_LOCAL_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryLocal : public Memory {
|
||||
public:
|
||||
MemoryLocal() = default;
|
||||
virtual ~MemoryLocal() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_LOCAL_H
|
60
libunwindstack/MemoryOffline.h
Normal file
60
libunwindstack/MemoryOffline.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_OFFLINE_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryRange.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryOffline : public Memory {
|
||||
public:
|
||||
MemoryOffline() = default;
|
||||
virtual ~MemoryOffline() = default;
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MemoryRange> memory_;
|
||||
};
|
||||
|
||||
class MemoryOfflineParts : public Memory {
|
||||
public:
|
||||
MemoryOfflineParts() = default;
|
||||
virtual ~MemoryOfflineParts();
|
||||
|
||||
void Add(MemoryOffline* memory) { memories_.push_back(memory); }
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::vector<MemoryOffline*> memories_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_H
|
43
libunwindstack/MemoryOfflineBuffer.h
Normal file
43
libunwindstack/MemoryOfflineBuffer.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryOfflineBuffer : public Memory {
|
||||
public:
|
||||
MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
|
||||
virtual ~MemoryOfflineBuffer() = default;
|
||||
|
||||
void Reset(const uint8_t* data, uint64_t start, uint64_t end);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
const uint8_t* data_;
|
||||
uint64_t start_;
|
||||
uint64_t end_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
|
66
libunwindstack/MemoryRange.h
Normal file
66
libunwindstack/MemoryRange.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_RANGE_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_RANGE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// MemoryRange maps one address range onto another.
|
||||
// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
|
||||
// such that range.read(offset) is equivalent to underlying.read(src_begin).
|
||||
class MemoryRange : public Memory {
|
||||
public:
|
||||
MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
|
||||
uint64_t offset);
|
||||
virtual ~MemoryRange() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
uint64_t offset() { return offset_; }
|
||||
uint64_t length() { return length_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Memory> memory_;
|
||||
uint64_t begin_;
|
||||
uint64_t length_;
|
||||
uint64_t offset_;
|
||||
};
|
||||
|
||||
class MemoryRanges : public Memory {
|
||||
public:
|
||||
MemoryRanges() = default;
|
||||
virtual ~MemoryRanges() = default;
|
||||
|
||||
void Insert(MemoryRange* memory);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_RANGE_H
|
45
libunwindstack/MemoryRemote.h
Normal file
45
libunwindstack/MemoryRemote.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 _LIBUNWINDSTACK_MEMORY_REMOTE_H
|
||||
#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryRemote : public Memory {
|
||||
public:
|
||||
MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
|
||||
virtual ~MemoryRemote() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
pid_t pid() { return pid_; }
|
||||
|
||||
private:
|
||||
pid_t pid_;
|
||||
std::atomic_uintptr_t read_redirect_func_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_REMOTE_H
|
|
@ -25,10 +25,11 @@
|
|||
#include <string>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class MemoryFileAtOffset;
|
||||
|
||||
struct MapInfo {
|
||||
MapInfo(MapInfo* map_info, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
|
||||
const char* name)
|
||||
|
|
|
@ -21,12 +21,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
@ -37,6 +33,9 @@ class Memory {
|
|||
|
||||
static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
|
||||
static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
|
||||
static std::shared_ptr<Memory> CreateOfflineMemory(const uint8_t* data, uint64_t start,
|
||||
uint64_t end);
|
||||
static std::unique_ptr<Memory> CreateFileMemory(const std::string& path, uint64_t offset);
|
||||
|
||||
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
|
||||
|
||||
|
@ -55,157 +54,6 @@ class Memory {
|
|||
}
|
||||
};
|
||||
|
||||
class MemoryCache : public Memory {
|
||||
public:
|
||||
MemoryCache(Memory* memory) : impl_(memory) {}
|
||||
virtual ~MemoryCache() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
void Clear() override { cache_.clear(); }
|
||||
|
||||
private:
|
||||
constexpr static size_t kCacheBits = 12;
|
||||
constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
|
||||
constexpr static size_t kCacheSize = 1 << kCacheBits;
|
||||
std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
|
||||
|
||||
std::unique_ptr<Memory> impl_;
|
||||
};
|
||||
|
||||
class MemoryBuffer : public Memory {
|
||||
public:
|
||||
MemoryBuffer() = default;
|
||||
virtual ~MemoryBuffer() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
uint8_t* GetPtr(size_t offset);
|
||||
|
||||
void Resize(size_t size) { raw_.resize(size); }
|
||||
|
||||
uint64_t Size() { return raw_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> raw_;
|
||||
};
|
||||
|
||||
class MemoryFileAtOffset : public Memory {
|
||||
public:
|
||||
MemoryFileAtOffset() = default;
|
||||
virtual ~MemoryFileAtOffset();
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset, uint64_t size = UINT64_MAX);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
size_t Size() { return size_; }
|
||||
|
||||
void Clear() override;
|
||||
|
||||
protected:
|
||||
size_t size_ = 0;
|
||||
size_t offset_ = 0;
|
||||
uint8_t* data_ = nullptr;
|
||||
};
|
||||
|
||||
class MemoryRemote : public Memory {
|
||||
public:
|
||||
MemoryRemote(pid_t pid) : pid_(pid), read_redirect_func_(0) {}
|
||||
virtual ~MemoryRemote() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
pid_t pid() { return pid_; }
|
||||
|
||||
private:
|
||||
pid_t pid_;
|
||||
std::atomic_uintptr_t read_redirect_func_;
|
||||
};
|
||||
|
||||
class MemoryLocal : public Memory {
|
||||
public:
|
||||
MemoryLocal() = default;
|
||||
virtual ~MemoryLocal() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
};
|
||||
|
||||
// MemoryRange maps one address range onto another.
|
||||
// The range [src_begin, src_begin + length) in the underlying Memory is mapped onto offset,
|
||||
// such that range.read(offset) is equivalent to underlying.read(src_begin).
|
||||
class MemoryRange : public Memory {
|
||||
public:
|
||||
MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t length,
|
||||
uint64_t offset);
|
||||
virtual ~MemoryRange() = default;
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
uint64_t offset() { return offset_; }
|
||||
uint64_t length() { return length_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Memory> memory_;
|
||||
uint64_t begin_;
|
||||
uint64_t length_;
|
||||
uint64_t offset_;
|
||||
};
|
||||
|
||||
class MemoryRanges : public Memory {
|
||||
public:
|
||||
MemoryRanges() = default;
|
||||
virtual ~MemoryRanges() = default;
|
||||
|
||||
void Insert(MemoryRange* memory);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::map<uint64_t, std::unique_ptr<MemoryRange>> maps_;
|
||||
};
|
||||
|
||||
class MemoryOffline : public Memory {
|
||||
public:
|
||||
MemoryOffline() = default;
|
||||
virtual ~MemoryOffline() = default;
|
||||
|
||||
bool Init(const std::string& file, uint64_t offset);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MemoryRange> memory_;
|
||||
};
|
||||
|
||||
class MemoryOfflineBuffer : public Memory {
|
||||
public:
|
||||
MemoryOfflineBuffer(const uint8_t* data, uint64_t start, uint64_t end);
|
||||
virtual ~MemoryOfflineBuffer() = default;
|
||||
|
||||
void Reset(const uint8_t* data, uint64_t start, uint64_t end);
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
const uint8_t* data_;
|
||||
uint64_t start_;
|
||||
uint64_t end_;
|
||||
};
|
||||
|
||||
class MemoryOfflineParts : public Memory {
|
||||
public:
|
||||
MemoryOfflineParts() = default;
|
||||
virtual ~MemoryOfflineParts();
|
||||
|
||||
void Add(MemoryOffline* memory) { memories_.push_back(memory); }
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::vector<MemoryOffline*> memories_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_H
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryBuffer.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryCache.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
#include "MemoryFileAtOffset.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
#include "MemoryLocal.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -18,9 +18,8 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryOfflineBuffer.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryOffline.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryFake.h"
|
||||
#include "MemoryRange.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -20,9 +20,8 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "MemoryFake.h"
|
||||
#include "MemoryRange.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include <android-base/file.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Memory.h>
|
||||
#include "MemoryRemote.h"
|
||||
|
||||
#include "MemoryFake.h"
|
||||
#include "TestUtils.h"
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include <unwindstack/MachineX86.h>
|
||||
#include <unwindstack/MachineX86_64.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/RegsArm.h>
|
||||
#include <unwindstack/RegsArm64.h>
|
||||
#include <unwindstack/RegsX86.h>
|
||||
|
@ -43,6 +42,7 @@
|
|||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "ElfTestUtils.h"
|
||||
#include "MemoryOffline.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
|
|
@ -35,11 +35,11 @@
|
|||
#include <android-base/threads.h>
|
||||
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "MemoryRemote.h"
|
||||
#include "TestUtils.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "ElfInterfaceArm.h"
|
||||
|
@ -105,14 +106,7 @@ int GetElfInfo(const char* file, uint64_t offset) {
|
|||
// Send all log messages to stdout.
|
||||
log_to_stdout(true);
|
||||
|
||||
MemoryFileAtOffset* memory = new MemoryFileAtOffset;
|
||||
if (!memory->Init(file, offset)) {
|
||||
// Initializatation failed.
|
||||
printf("Failed to init\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Elf elf(memory);
|
||||
Elf elf(Memory::CreateFileMemory(file, offset).release());
|
||||
if (!elf.Init() || !elf.valid()) {
|
||||
printf("%s is not a valid elf file.\n", file);
|
||||
return 1;
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
#include <unwindstack/Log.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "ArmExidx.h"
|
||||
#include "DwarfOp.h"
|
||||
|
@ -165,14 +166,7 @@ void PrintArmRegInformation(ElfInterfaceArm* interface, uint64_t pc) {
|
|||
}
|
||||
|
||||
int GetInfo(const char* file, uint64_t pc) {
|
||||
MemoryFileAtOffset* memory = new MemoryFileAtOffset;
|
||||
if (!memory->Init(file, 0)) {
|
||||
// Initializatation failed.
|
||||
printf("Failed to init\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Elf elf(memory);
|
||||
Elf elf(Memory::CreateFileMemory(file, pc).release());
|
||||
if (!elf.Init() || !elf.valid()) {
|
||||
printf("%s is not a valid elf file.\n", file);
|
||||
return 1;
|
||||
|
@ -205,7 +199,7 @@ int GetInfo(const char* file, uint64_t pc) {
|
|||
DwarfSection* section = interface->eh_frame();
|
||||
if (section != nullptr) {
|
||||
printf("\neh_frame:\n");
|
||||
PrintRegInformation(section, memory, pc, elf.class_type());
|
||||
PrintRegInformation(section, elf.memory(), pc, elf.class_type());
|
||||
} else {
|
||||
printf("\nno eh_frame information\n");
|
||||
}
|
||||
|
@ -213,7 +207,7 @@ int GetInfo(const char* file, uint64_t pc) {
|
|||
section = interface->debug_frame();
|
||||
if (section != nullptr) {
|
||||
printf("\ndebug_frame:\n");
|
||||
PrintRegInformation(section, memory, pc, elf.class_type());
|
||||
PrintRegInformation(section, elf.memory(), pc, elf.class_type());
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("\nno debug_frame information\n");
|
||||
|
|
|
@ -59,13 +59,7 @@ int main(int argc, char** argv) {
|
|||
// Send all log messages to stdout.
|
||||
unwindstack::log_to_stdout(true);
|
||||
|
||||
unwindstack::MemoryFileAtOffset* memory = new unwindstack::MemoryFileAtOffset;
|
||||
if (!memory->Init(argv[1], 0)) {
|
||||
printf("Failed to init\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unwindstack::Elf elf(memory);
|
||||
unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
|
||||
if (!elf.Init() || !elf.valid()) {
|
||||
printf("%s is not a valid elf file.\n", argv[1]);
|
||||
return 1;
|
||||
|
|
Loading…
Reference in a new issue