Move dex pc frame creation into libunwindstack.
Test: Compiles, all unit tests pass. Test: Ran 137-cfi art test in interpreter and verified interpreter Test: frames still show up. Change-Id: Icea90194986faa733a873e8cf467fc2513eb5573
This commit is contained in:
parent
01ba115732
commit
d70ea5ea85
13 changed files with 367 additions and 235 deletions
|
@ -50,7 +50,6 @@ libbacktrace_sources = [
|
|||
"BacktracePtrace.cpp",
|
||||
"thread_utils.c",
|
||||
"ThreadEntry.cpp",
|
||||
"UnwindDexFile.cpp",
|
||||
"UnwindStack.cpp",
|
||||
"UnwindStackMap.cpp",
|
||||
]
|
||||
|
@ -110,10 +109,9 @@ cc_library {
|
|||
static_libs: ["libasync_safe"],
|
||||
},
|
||||
vendor: {
|
||||
cflags: ["-DNO_LIBDEXFILE"],
|
||||
exclude_srcs: ["UnwindDexFile.cpp"],
|
||||
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
|
||||
exclude_shared_libs: ["libdexfile"],
|
||||
},
|
||||
}
|
||||
},
|
||||
whole_static_libs: ["libdemangle"],
|
||||
}
|
||||
|
@ -145,8 +143,6 @@ cc_test {
|
|||
"backtrace_test.cpp",
|
||||
"GetPss.cpp",
|
||||
"thread_utils.c",
|
||||
|
||||
"unwind_dex_test.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
|
|
|
@ -36,70 +36,15 @@
|
|||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#endif
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#ifndef NO_LIBDEXFILE
|
||||
#include "UnwindDexFile.h"
|
||||
#endif
|
||||
#include "UnwindStack.h"
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
static void FillInDexFrame(UnwindStackMap* stack_map, uint64_t dex_pc,
|
||||
backtrace_frame_data_t* frame) {
|
||||
// The DEX PC points into the .dex section within an ELF file.
|
||||
// However, this is a BBS section manually mmaped to a .vdex file,
|
||||
// so we need to get the following map to find the ELF data.
|
||||
unwindstack::Maps* maps = stack_map->stack_maps();
|
||||
auto it = maps->begin();
|
||||
uint64_t rel_dex_pc;
|
||||
unwindstack::MapInfo* info;
|
||||
for (; it != maps->end(); ++it) {
|
||||
auto entry = *it;
|
||||
if (dex_pc >= entry->start && dex_pc < entry->end) {
|
||||
info = entry;
|
||||
rel_dex_pc = dex_pc - entry->start;
|
||||
frame->map.start = entry->start;
|
||||
frame->map.end = entry->end;
|
||||
frame->map.offset = entry->offset;
|
||||
frame->map.load_bias = entry->load_bias;
|
||||
frame->map.flags = entry->flags;
|
||||
frame->map.name = entry->name;
|
||||
frame->rel_pc = rel_dex_pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (it == maps->end() || ++it == maps->end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto entry = *it;
|
||||
auto process_memory = stack_map->process_memory();
|
||||
unwindstack::Elf* elf = entry->GetElf(process_memory, true);
|
||||
if (!elf->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the relative dex by the offset.
|
||||
rel_dex_pc += entry->elf_offset;
|
||||
|
||||
uint64_t dex_offset;
|
||||
if (!elf->GetFunctionName(rel_dex_pc, &frame->func_name, &dex_offset)) {
|
||||
return;
|
||||
}
|
||||
frame->func_offset = dex_offset;
|
||||
if (frame->func_name != "$dexfile") {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NO_LIBDEXFILE
|
||||
UnwindDexFile* dex_file = stack_map->GetDexFile(dex_pc - dex_offset, info);
|
||||
if (dex_file != nullptr) {
|
||||
dex_file->GetMethodInformation(dex_offset, &frame->func_name, &frame->func_offset);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
|
||||
std::vector<std::string>* skip_names, BacktraceUnwindError* error) {
|
||||
|
@ -110,6 +55,11 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
if (stack_map->GetJitDebug() != nullptr) {
|
||||
unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
|
||||
}
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
if (stack_map->GetDexFiles() != nullptr) {
|
||||
unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
|
||||
}
|
||||
#endif
|
||||
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
|
||||
if (error != nullptr) {
|
||||
switch (unwinder.LastErrorCode()) {
|
||||
|
@ -150,36 +100,11 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
}
|
||||
|
||||
auto unwinder_frames = unwinder.frames();
|
||||
// Get the real number of frames we'll need.
|
||||
size_t total_frames = 0;
|
||||
for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, total_frames++) {
|
||||
if (unwinder_frames[i].dex_pc != 0) {
|
||||
total_frames++;
|
||||
}
|
||||
}
|
||||
frames->resize(total_frames);
|
||||
frames->resize(unwinder.NumFrames() - num_ignore_frames);
|
||||
size_t cur_frame = 0;
|
||||
for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++) {
|
||||
auto frame = &unwinder_frames[i];
|
||||
|
||||
// Inject extra 'virtual' frame that represents the dex pc data.
|
||||
// The dex pc is magic register defined in the Mterp interpreter,
|
||||
// and thus it will be restored/observed in the frame after it.
|
||||
// Adding the dex frame first here will create something like:
|
||||
// #7 pc 006b1ba1 libartd.so ExecuteMterpImpl+14625
|
||||
// #8 pc 0015fa20 core.vdex java.util.Arrays.binarySearch+8
|
||||
// #9 pc 0039a1ef libartd.so art::interpreter::Execute+719
|
||||
if (frame->dex_pc != 0) {
|
||||
backtrace_frame_data_t* dex_frame = &frames->at(cur_frame);
|
||||
dex_frame->num = cur_frame++;
|
||||
dex_frame->pc = frame->dex_pc;
|
||||
dex_frame->rel_pc = frame->dex_pc;
|
||||
dex_frame->sp = frame->sp;
|
||||
dex_frame->stack_size = 0;
|
||||
dex_frame->func_offset = 0;
|
||||
FillInDexFrame(stack_map, frame->dex_pc, dex_frame);
|
||||
}
|
||||
|
||||
backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
|
||||
|
||||
back_frame->num = cur_frame++;
|
||||
|
|
|
@ -26,20 +26,11 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include "UnwindDexFile.h"
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
|
||||
|
||||
UnwindStackMap::~UnwindStackMap() {
|
||||
#ifndef NO_LIBDEXFILE
|
||||
for (auto& entry : dex_files_) {
|
||||
delete entry.second;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UnwindStackMap::Build() {
|
||||
if (pid_ == 0) {
|
||||
pid_ = getpid();
|
||||
|
@ -54,6 +45,9 @@ bool UnwindStackMap::Build() {
|
|||
// Create a JitDebug object for getting jit unwind information.
|
||||
std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
|
||||
jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
dex_files_.reset(new unwindstack::DexFiles(process_memory_));
|
||||
#endif
|
||||
|
||||
if (!stack_maps_->Parse()) {
|
||||
return false;
|
||||
|
@ -127,26 +121,6 @@ std::shared_ptr<unwindstack::Memory> UnwindStackMap::GetProcessMemory() {
|
|||
return process_memory_;
|
||||
}
|
||||
|
||||
#ifdef NO_LIBDEXFILE
|
||||
UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t, unwindstack::MapInfo*) {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info) {
|
||||
// Lock while we get the data.
|
||||
std::lock_guard<std::mutex> guard(dex_lock_);
|
||||
UnwindDexFile* dex_file;
|
||||
auto entry = dex_files_.find(dex_file_offset);
|
||||
if (entry == dex_files_.end()) {
|
||||
dex_file = UnwindDexFile::Create(dex_file_offset, process_memory_.get(), info);
|
||||
dex_files_[dex_file_offset] = dex_file;
|
||||
} else {
|
||||
dex_file = entry->second;
|
||||
}
|
||||
return dex_file;
|
||||
}
|
||||
#endif
|
||||
|
||||
UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {}
|
||||
|
||||
bool UnwindStackOfflineMap::Build() {
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#endif
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
|
@ -36,7 +39,7 @@ class UnwindDexFile;
|
|||
class UnwindStackMap : public BacktraceMap {
|
||||
public:
|
||||
explicit UnwindStackMap(pid_t pid);
|
||||
~UnwindStackMap();
|
||||
~UnwindStackMap() = default;
|
||||
|
||||
bool Build() override;
|
||||
|
||||
|
@ -51,7 +54,9 @@ class UnwindStackMap : public BacktraceMap {
|
|||
|
||||
unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
|
||||
|
||||
UnwindDexFile* GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info);
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint64_t GetLoadBias(size_t index) override;
|
||||
|
@ -59,9 +64,8 @@ class UnwindStackMap : public BacktraceMap {
|
|||
std::unique_ptr<unwindstack::Maps> stack_maps_;
|
||||
std::shared_ptr<unwindstack::Memory> process_memory_;
|
||||
std::unique_ptr<unwindstack::JitDebug> jit_debug_;
|
||||
#ifndef NO_LIBDEXFILE
|
||||
std::mutex dex_lock_;
|
||||
std::unordered_map<uint64_t, UnwindDexFile*> dex_files_;
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
std::unique_ptr<unwindstack::DexFiles> dex_files_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -80,7 +80,13 @@ cc_library {
|
|||
host: {
|
||||
cflags: ["-O0", "-g"],
|
||||
},
|
||||
vendor: {
|
||||
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
|
||||
exclude_static_libs: ["libunwindstack_dex"],
|
||||
exclude_shared_libs: ["libdexfile"],
|
||||
},
|
||||
},
|
||||
whole_static_libs: ["libunwindstack_dex"],
|
||||
|
||||
arch: {
|
||||
x86: {
|
||||
|
@ -99,14 +105,70 @@ cc_library {
|
|||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libdexfile",
|
||||
"liblog",
|
||||
"liblzma",
|
||||
],
|
||||
}
|
||||
|
||||
// Isolate the dex file processing into a separate library. Currently,
|
||||
// it is necessary to add art include directories directly, which also
|
||||
// adds the art elf.h file in the include path, overriding the system one.
|
||||
// Work to isolate libdexfile is b/72216369.
|
||||
cc_library_static {
|
||||
name: "libunwindstack_dex",
|
||||
vendor_available: false,
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
cflags: [
|
||||
"-Wexit-time-destructors",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"DexFile.cpp",
|
||||
"DexFiles.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libdexfile",
|
||||
],
|
||||
local_include_dirs: ["include"],
|
||||
allow_undefined_symbols: true,
|
||||
|
||||
// libdexfile will eventually properly export headers, for now include
|
||||
// these directly.
|
||||
include_dirs: [
|
||||
"art/runtime",
|
||||
],
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Unit Tests
|
||||
//-------------------------------------------------------------------------
|
||||
cc_test_library {
|
||||
name: "libunwindstack_dex_test",
|
||||
vendor_available: false,
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
||||
srcs: [
|
||||
"tests/DexFileTest.cpp",
|
||||
],
|
||||
local_include_dirs: ["include"],
|
||||
allow_undefined_symbols: true,
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
// libdexfile will eventually properly export headers, for now include
|
||||
// these directly.
|
||||
include_dirs: [
|
||||
"art/runtime",
|
||||
],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libunwindstack_test",
|
||||
defaults: ["libunwindstack_flags"],
|
||||
|
@ -168,6 +230,8 @@ cc_test {
|
|||
"libgmock",
|
||||
],
|
||||
|
||||
whole_static_libs: ["libunwindstack_dex_test"],
|
||||
|
||||
data: [
|
||||
"tests/files/elf32.xz",
|
||||
"tests/files/elf64.xz",
|
||||
|
|
|
@ -33,26 +33,27 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "UnwindDexFile.h"
|
||||
#include "DexFile.h"
|
||||
|
||||
UnwindDexFile* UnwindDexFile::Create(uint64_t dex_file_offset_in_memory,
|
||||
unwindstack::Memory* memory, unwindstack::MapInfo* info) {
|
||||
namespace unwindstack {
|
||||
|
||||
DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) {
|
||||
if (!info->name.empty()) {
|
||||
std::unique_ptr<UnwindDexFileFromFile> dex_file(new UnwindDexFileFromFile);
|
||||
std::unique_ptr<DexFileFromFile> dex_file(new DexFileFromFile);
|
||||
if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
|
||||
return dex_file.release();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<UnwindDexFileFromMemory> dex_file(new UnwindDexFileFromMemory);
|
||||
std::unique_ptr<DexFileFromMemory> dex_file(new DexFileFromMemory);
|
||||
if (dex_file->Open(dex_file_offset_in_memory, memory)) {
|
||||
return dex_file.release();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
|
||||
uint64_t* method_offset) {
|
||||
void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
|
||||
uint64_t* method_offset) {
|
||||
if (dex_file_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
@ -87,13 +88,13 @@ void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* metho
|
|||
}
|
||||
}
|
||||
|
||||
UnwindDexFileFromFile::~UnwindDexFileFromFile() {
|
||||
DexFileFromFile::~DexFileFromFile() {
|
||||
if (size_ != 0) {
|
||||
munmap(mapped_memory_, size_);
|
||||
}
|
||||
}
|
||||
|
||||
bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
|
||||
bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
|
@ -137,7 +138,7 @@ bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::st
|
|||
return dex_file_ != nullptr;
|
||||
}
|
||||
|
||||
bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory) {
|
||||
bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
|
||||
art::DexFile::Header header;
|
||||
if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
|
||||
return false;
|
||||
|
@ -160,3 +161,5 @@ bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindsta
|
|||
dex_file_.reset(dex.release());
|
||||
return dex_file_ != nullptr;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -14,8 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWIND_DEX_FILE_H
|
||||
#define _LIBBACKTRACE_UNWIND_DEX_FILE_H
|
||||
#ifndef _LIBUNWINDSTACK_DEX_FILE_H
|
||||
#define _LIBUNWINDSTACK_DEX_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -26,28 +26,24 @@
|
|||
#include <dex/dex_file-inl.h>
|
||||
|
||||
namespace unwindstack {
|
||||
class Memory;
|
||||
struct MapInfo;
|
||||
} // namespace unwindstack
|
||||
|
||||
class UnwindDexFile {
|
||||
class DexFile {
|
||||
public:
|
||||
UnwindDexFile() = default;
|
||||
virtual ~UnwindDexFile() = default;
|
||||
DexFile() = default;
|
||||
virtual ~DexFile() = default;
|
||||
|
||||
void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
|
||||
|
||||
static UnwindDexFile* Create(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory,
|
||||
unwindstack::MapInfo* info);
|
||||
static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<const art::DexFile> dex_file_;
|
||||
};
|
||||
|
||||
class UnwindDexFileFromFile : public UnwindDexFile {
|
||||
class DexFileFromFile : public DexFile {
|
||||
public:
|
||||
UnwindDexFileFromFile() = default;
|
||||
virtual ~UnwindDexFileFromFile();
|
||||
DexFileFromFile() = default;
|
||||
virtual ~DexFileFromFile();
|
||||
|
||||
bool Open(uint64_t dex_file_offset_in_file, const std::string& name);
|
||||
|
||||
|
@ -56,15 +52,17 @@ class UnwindDexFileFromFile : public UnwindDexFile {
|
|||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
class UnwindDexFileFromMemory : public UnwindDexFile {
|
||||
class DexFileFromMemory : public DexFile {
|
||||
public:
|
||||
UnwindDexFileFromMemory() = default;
|
||||
virtual ~UnwindDexFileFromMemory() = default;
|
||||
DexFileFromMemory() = default;
|
||||
virtual ~DexFileFromMemory() = default;
|
||||
|
||||
bool Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory);
|
||||
bool Open(uint64_t dex_file_offset_in_memory, Memory* memory);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> memory_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_DEX_FILE_H
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DEX_FILE_H
|
65
libunwindstack/DexFiles.cpp
Normal file
65
libunwindstack/DexFiles.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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 <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "DexFile.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
|
||||
|
||||
DexFiles::~DexFiles() {
|
||||
for (auto& entry : files_) {
|
||||
delete entry.second;
|
||||
}
|
||||
}
|
||||
|
||||
DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
|
||||
// Lock while processing the data.
|
||||
std::lock_guard<std::mutex> guard(files_lock_);
|
||||
DexFile* dex_file;
|
||||
auto entry = files_.find(dex_file_offset);
|
||||
if (entry == files_.end()) {
|
||||
dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
|
||||
files_[dex_file_offset] = dex_file;
|
||||
} else {
|
||||
dex_file = entry->second;
|
||||
}
|
||||
return dex_file;
|
||||
}
|
||||
|
||||
void DexFiles::GetMethodInformation(uint64_t dex_offset, MapInfo* info, std::string* method_name,
|
||||
uint64_t* method_offset) {
|
||||
DexFile* dex_file = GetDexFile(dex_offset, info);
|
||||
if (dex_file != nullptr) {
|
||||
dex_file->GetMethodInformation(dex_offset, method_name, method_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void DexFiles::SetArch(ArchEnum) {}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -31,8 +31,74 @@
|
|||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
#include <unwindstack/DexFiles.h>
|
||||
#endif
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Inject extra 'virtual' frame that represents the dex pc data.
|
||||
// The dex pc is a magic register defined in the Mterp interpreter,
|
||||
// and thus it will be restored/observed in the frame after it.
|
||||
// Adding the dex frame first here will create something like:
|
||||
// #7 pc 0015fa20 core.vdex java.util.Arrays.binarySearch+8
|
||||
// #8 pc 006b1ba1 libartd.so ExecuteMterpImpl+14625
|
||||
// #9 pc 0039a1ef libartd.so art::interpreter::Execute+719
|
||||
void Unwinder::FillInDexFrame() {
|
||||
size_t frame_num = frames_.size();
|
||||
frames_.resize(frame_num + 1);
|
||||
FrameData* frame = &frames_.at(frame_num);
|
||||
|
||||
uint64_t dex_pc = regs_->dex_pc();
|
||||
frame->pc = dex_pc;
|
||||
frame->sp = regs_->sp();
|
||||
|
||||
auto it = maps_->begin();
|
||||
uint64_t rel_dex_pc;
|
||||
MapInfo* info;
|
||||
for (; it != maps_->end(); ++it) {
|
||||
auto entry = *it;
|
||||
if (dex_pc >= entry->start && dex_pc < entry->end) {
|
||||
info = entry;
|
||||
rel_dex_pc = dex_pc - entry->start;
|
||||
frame->map_start = entry->start;
|
||||
frame->map_end = entry->end;
|
||||
frame->map_offset = entry->offset;
|
||||
frame->map_load_bias = entry->load_bias;
|
||||
frame->map_flags = entry->flags;
|
||||
frame->map_name = entry->name;
|
||||
frame->rel_pc = rel_dex_pc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == maps_->end() || ++it == maps_->end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto entry = *it;
|
||||
unwindstack::Elf* elf = entry->GetElf(process_memory_, true);
|
||||
if (!elf->valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the relative dex by the offset.
|
||||
rel_dex_pc += entry->elf_offset;
|
||||
|
||||
uint64_t dex_offset;
|
||||
if (!elf->GetFunctionName(rel_dex_pc, &frame->function_name, &dex_offset)) {
|
||||
return;
|
||||
}
|
||||
frame->function_offset = dex_offset;
|
||||
if (frame->function_name != "$dexfile") {
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
dex_files_->GetMethodInformation(dex_offset, info, &frame->function_name, &frame->function_offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) {
|
||||
size_t frame_num = frames_.size();
|
||||
frames_.resize(frame_num + 1);
|
||||
|
@ -40,7 +106,6 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc
|
|||
frame->num = frame_num;
|
||||
frame->sp = regs_->sp();
|
||||
frame->rel_pc = adjusted_rel_pc;
|
||||
frame->dex_pc = regs_->dex_pc();
|
||||
|
||||
if (map_info == nullptr) {
|
||||
frame->pc = regs_->pc();
|
||||
|
@ -128,6 +193,11 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
|
|||
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
|
||||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
|
||||
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
|
||||
if (regs_->dex_pc() != 0) {
|
||||
// Add a frame to represent the dex file.
|
||||
FillInDexFrame();
|
||||
}
|
||||
|
||||
FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
|
||||
|
||||
// Once a frame is added, stop skipping frames.
|
||||
|
@ -240,4 +310,11 @@ void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
|
|||
jit_debug_ = jit_debug;
|
||||
}
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
|
||||
dex_files->SetArch(arch);
|
||||
dex_files_ = dex_files;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
56
libunwindstack/include/unwindstack/DexFiles.h
Normal file
56
libunwindstack/include/unwindstack/DexFiles.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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_DEX_FILES_H
|
||||
#define _LIBUNWINDSTACK_DEX_FILES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
class DexFile;
|
||||
class Maps;
|
||||
struct MapInfo;
|
||||
class Memory;
|
||||
enum ArchEnum : uint8_t;
|
||||
|
||||
class DexFiles {
|
||||
public:
|
||||
explicit DexFiles(std::shared_ptr<Memory>& memory);
|
||||
~DexFiles();
|
||||
|
||||
DexFile* GetDexFile(uint64_t dex_offset, MapInfo* info);
|
||||
|
||||
void GetMethodInformation(uint64_t dex_offset, MapInfo* info, std::string* method_name,
|
||||
uint64_t* method_offset);
|
||||
|
||||
void SetArch(ArchEnum arch);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Memory> memory_;
|
||||
std::mutex files_lock_;
|
||||
std::unordered_map<uint64_t, DexFile*> files_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DEX_FILES_H
|
|
@ -32,6 +32,7 @@
|
|||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
class DexFiles;
|
||||
class Elf;
|
||||
class JitDebug;
|
||||
enum ArchEnum : uint8_t;
|
||||
|
@ -42,7 +43,6 @@ struct FrameData {
|
|||
uint64_t rel_pc;
|
||||
uint64_t pc;
|
||||
uint64_t sp;
|
||||
uint64_t dex_pc;
|
||||
|
||||
std::string function_name;
|
||||
uint64_t function_offset;
|
||||
|
@ -75,10 +75,15 @@ class Unwinder {
|
|||
|
||||
void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
|
||||
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
|
||||
#endif
|
||||
|
||||
ErrorCode LastErrorCode() { return last_error_.code; }
|
||||
uint64_t LastErrorAddress() { return last_error_.address; }
|
||||
|
||||
private:
|
||||
void FillInDexFrame();
|
||||
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
|
||||
|
||||
size_t max_frames_;
|
||||
|
@ -87,6 +92,9 @@ class Unwinder {
|
|||
std::vector<FrameData> frames_;
|
||||
std::shared_ptr<Memory> process_memory_;
|
||||
JitDebug* jit_debug_ = nullptr;
|
||||
#if !defined(NO_LIBDEXFILE_SUPPORT)
|
||||
DexFiles* dex_files_ = nullptr;
|
||||
#endif
|
||||
ErrorData last_error_;
|
||||
};
|
||||
|
||||
|
|
|
@ -30,46 +30,11 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "UnwindDexFile.h"
|
||||
#include "DexFile.h"
|
||||
|
||||
class MemoryFake : public unwindstack::Memory {
|
||||
public:
|
||||
MemoryFake() = default;
|
||||
virtual ~MemoryFake() = default;
|
||||
#include "MemoryFake.h"
|
||||
|
||||
size_t Read(uint64_t addr, void* buffer, size_t size) override;
|
||||
|
||||
void SetMemory(uint64_t addr, const void* memory, size_t length);
|
||||
|
||||
void Clear() { data_.clear(); }
|
||||
|
||||
private:
|
||||
std::unordered_map<uint64_t, uint8_t> data_;
|
||||
};
|
||||
|
||||
void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) {
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(memory);
|
||||
for (size_t i = 0; i < length; i++, addr++) {
|
||||
auto value = data_.find(addr);
|
||||
if (value != data_.end()) {
|
||||
value->second = src[i];
|
||||
} else {
|
||||
data_.insert({addr, src[i]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) {
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>(memory);
|
||||
for (size_t i = 0; i < size; i++, addr++) {
|
||||
auto value = data_.find(addr);
|
||||
if (value == data_.end()) {
|
||||
return i;
|
||||
}
|
||||
dst[i] = value->second;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
namespace unwindstack {
|
||||
|
||||
// Borrowed from art/dex/dex_file_test.cc.
|
||||
static constexpr uint32_t kDexData[] = {
|
||||
|
@ -92,12 +57,12 @@ static constexpr uint32_t kDexData[] = {
|
|||
0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
|
||||
};
|
||||
|
||||
TEST(UnwindDexTest, from_file_open_non_exist) {
|
||||
UnwindDexFileFromFile dex_file;
|
||||
TEST(DexFileTest, from_file_open_non_exist) {
|
||||
DexFileFromFile dex_file;
|
||||
ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_file_open_too_small) {
|
||||
TEST(DexFileTest, from_file_open_too_small) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
|
@ -106,7 +71,7 @@ TEST(UnwindDexTest, from_file_open_too_small) {
|
|||
TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2)));
|
||||
|
||||
// Header too small.
|
||||
UnwindDexFileFromFile dex_file;
|
||||
DexFileFromFile dex_file;
|
||||
ASSERT_FALSE(dex_file.Open(0, tf.path));
|
||||
|
||||
// Header correct, file too small.
|
||||
|
@ -116,18 +81,18 @@ TEST(UnwindDexTest, from_file_open_too_small) {
|
|||
ASSERT_FALSE(dex_file.Open(0, tf.path));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_file_open) {
|
||||
TEST(DexFileTest, from_file_open) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
UnwindDexFileFromFile dex_file;
|
||||
DexFileFromFile dex_file;
|
||||
ASSERT_TRUE(dex_file.Open(0, tf.path));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_file_open_non_zero_offset) {
|
||||
TEST(DexFileTest, from_file_open_non_zero_offset) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
|
@ -135,38 +100,38 @@ TEST(UnwindDexTest, from_file_open_non_zero_offset) {
|
|||
ASSERT_EQ(sizeof(kDexData),
|
||||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
UnwindDexFileFromFile dex_file;
|
||||
DexFileFromFile dex_file;
|
||||
ASSERT_TRUE(dex_file.Open(0x100, tf.path));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_memory_fail_too_small_for_header) {
|
||||
TEST(DexFileTest, from_memory_fail_too_small_for_header) {
|
||||
MemoryFake memory;
|
||||
|
||||
memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1);
|
||||
UnwindDexFileFromMemory dex_file;
|
||||
DexFileFromMemory dex_file;
|
||||
|
||||
ASSERT_FALSE(dex_file.Open(0x1000, &memory));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_memory_fail_too_small_for_data) {
|
||||
TEST(DexFileTest, from_memory_fail_too_small_for_data) {
|
||||
MemoryFake memory;
|
||||
|
||||
memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2);
|
||||
UnwindDexFileFromMemory dex_file;
|
||||
DexFileFromMemory dex_file;
|
||||
|
||||
ASSERT_FALSE(dex_file.Open(0x1000, &memory));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, from_memory_open) {
|
||||
TEST(DexFileTest, from_memory_open) {
|
||||
MemoryFake memory;
|
||||
|
||||
memory.SetMemory(0x1000, kDexData, sizeof(kDexData));
|
||||
UnwindDexFileFromMemory dex_file;
|
||||
DexFileFromMemory dex_file;
|
||||
|
||||
ASSERT_TRUE(dex_file.Open(0x1000, &memory));
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_file) {
|
||||
TEST(DexFileTest, create_using_file) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
|
@ -175,12 +140,12 @@ TEST(UnwindDexTest, create_using_file) {
|
|||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
MemoryFake memory;
|
||||
unwindstack::MapInfo info(0, 0x10000, 0, 0x5, tf.path);
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x500, &memory, &info));
|
||||
MapInfo info(0, 0x10000, 0, 0x5, tf.path);
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x500, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_file_non_zero_start) {
|
||||
TEST(DexFileTest, create_using_file_non_zero_start) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
|
@ -189,12 +154,12 @@ TEST(UnwindDexTest, create_using_file_non_zero_start) {
|
|||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
MemoryFake memory;
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x600, &memory, &info));
|
||||
MapInfo info(0x100, 0x10000, 0, 0x5, tf.path);
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x600, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_file_non_zero_offset) {
|
||||
TEST(DexFileTest, create_using_file_non_zero_offset) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
|
@ -203,28 +168,28 @@ TEST(UnwindDexTest, create_using_file_non_zero_offset) {
|
|||
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
|
||||
|
||||
MemoryFake memory;
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x400, &memory, &info));
|
||||
MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path);
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x400, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_memory_empty_file) {
|
||||
TEST(DexFileTest, create_using_memory_empty_file) {
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_memory_file_does_not_exist) {
|
||||
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, create_using_memory_file_is_malformed) {
|
||||
TEST(DexFileTest, create_using_memory_file_is_malformed) {
|
||||
TemporaryFile tf;
|
||||
ASSERT_TRUE(tf.fd != -1);
|
||||
|
||||
|
@ -233,30 +198,30 @@ TEST(UnwindDexTest, create_using_memory_file_is_malformed) {
|
|||
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
|
||||
// Check it came from memory by clearing memory and verifying it fails.
|
||||
memory.Clear();
|
||||
dex_file.reset(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
dex_file.reset(DexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file == nullptr);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, get_method_not_opened) {
|
||||
TEST(DexFileTest, get_method_not_opened) {
|
||||
std::string method("something");
|
||||
uint64_t method_offset = 100;
|
||||
UnwindDexFile dex_file;
|
||||
DexFile dex_file;
|
||||
dex_file.GetMethodInformation(0x100, &method, &method_offset);
|
||||
EXPECT_EQ("something", method);
|
||||
EXPECT_EQ(100U, method_offset);
|
||||
}
|
||||
|
||||
TEST(UnwindDexTest, get_method) {
|
||||
TEST(DexFileTest, get_method) {
|
||||
MemoryFake memory;
|
||||
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
|
||||
unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
|
||||
std::unique_ptr<UnwindDexFile> dex_file(UnwindDexFile::Create(0x4000, &memory, &info));
|
||||
MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
|
||||
std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
|
||||
ASSERT_TRUE(dex_file != nullptr);
|
||||
|
||||
std::string method;
|
||||
|
@ -271,3 +236,5 @@ TEST(UnwindDexTest, get_method) {
|
|||
EXPECT_EQ("not_in_a_method", method);
|
||||
EXPECT_EQ(0x123U, method_offset);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -96,13 +96,8 @@ void DoUnwind(pid_t pid) {
|
|||
unwinder.Unwind();
|
||||
|
||||
// Print the frames.
|
||||
const std::vector<unwindstack::FrameData>& frames = unwinder.frames();
|
||||
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
|
||||
printf("%s\n", unwinder.FormatFrame(i).c_str());
|
||||
const unwindstack::FrameData* frame = &frames[i];
|
||||
if (frame->dex_pc != 0) {
|
||||
printf(" dex pc %" PRIx64 "\n", frame->dex_pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue