Merge "Internalize subclasses of Memory"
am: 5fb660930c
Change-Id: I1fc6df9d1c6a195e78e62a6361ad1c9df0e28a8c
This commit is contained in:
commit
7ad4c6b7f8
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 New Issue