Merge "Add support for the new unwind method."
This commit is contained in:
commit
9ccf627e78
|
@ -53,6 +53,8 @@ libbacktrace_sources = [
|
|||
"UnwindCurrent.cpp",
|
||||
"UnwindMap.cpp",
|
||||
"UnwindPtrace.cpp",
|
||||
"UnwindStack.cpp",
|
||||
"UnwindStackMap.cpp",
|
||||
]
|
||||
|
||||
cc_library_headers {
|
||||
|
@ -84,6 +86,7 @@ cc_library {
|
|||
"libbase",
|
||||
"liblog",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
static_libs: ["libcutils"],
|
||||
|
@ -97,6 +100,7 @@ cc_library {
|
|||
"libbase",
|
||||
"liblog",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
static_libs: ["libcutils"],
|
||||
|
@ -108,6 +112,7 @@ cc_library {
|
|||
"libbase",
|
||||
"liblog",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
static_libs: ["libasync_safe", "libcutils"],
|
||||
|
@ -130,11 +135,13 @@ cc_library_shared {
|
|||
linux: {
|
||||
shared_libs: [
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
],
|
||||
},
|
||||
android: {
|
||||
shared_libs: [
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@ -161,6 +168,7 @@ cc_library_static {
|
|||
shared_libs = [
|
||||
"libbase",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
"libziparchive",
|
||||
],
|
||||
}
|
||||
|
@ -192,6 +200,7 @@ cc_test {
|
|||
"libcutils",
|
||||
"liblog",
|
||||
"libunwind",
|
||||
"libunwindstack",
|
||||
],
|
||||
|
||||
group_static_libs: true,
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#include <cutils/threads.h>
|
||||
#endif
|
||||
|
||||
#include <backtrace/Backtrace.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
|
||||
#include "BacktraceLog.h"
|
||||
#include "UnwindStack.h"
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
static std::string GetFunctionName(pid_t pid, BacktraceMap* back_map, uintptr_t pc,
|
||||
uintptr_t* offset) {
|
||||
*offset = 0;
|
||||
unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
|
||||
|
||||
// Get the map for this
|
||||
unwindstack::MapInfo* map_info = maps->Find(pc);
|
||||
if (map_info == nullptr || map_info->flags & PROT_DEVICE_MAP) {
|
||||
return "";
|
||||
}
|
||||
|
||||
unwindstack::Elf* elf = map_info->GetElf(pid, true);
|
||||
|
||||
std::string name;
|
||||
uint64_t func_offset;
|
||||
if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
|
||||
return "";
|
||||
}
|
||||
*offset = func_offset;
|
||||
return name;
|
||||
}
|
||||
|
||||
static bool IsUnwindLibrary(const std::string& map_name) {
|
||||
const std::string library(basename(map_name.c_str()));
|
||||
return library == "libunwindstack.so" || library == "libbacktrace.so";
|
||||
}
|
||||
|
||||
static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* regs,
|
||||
BacktraceMap* back_map, std::vector<backtrace_frame_data_t>* frames,
|
||||
size_t num_ignore_frames) {
|
||||
unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
|
||||
bool adjust_rel_pc = false;
|
||||
size_t num_frames = 0;
|
||||
frames->clear();
|
||||
while (num_frames < MAX_BACKTRACE_FRAMES) {
|
||||
if (regs->pc() == 0) {
|
||||
break;
|
||||
}
|
||||
unwindstack::MapInfo* map_info = maps->Find(regs->pc());
|
||||
if (map_info == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
unwindstack::Elf* elf = map_info->GetElf(pid, true);
|
||||
uint64_t rel_pc = regs->pc();
|
||||
if (map_info != nullptr) {
|
||||
rel_pc = elf->GetRelPc(regs->pc(), map_info);
|
||||
}
|
||||
|
||||
bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
|
||||
if (num_ignore_frames == 0 && !skip_frame) {
|
||||
uint64_t adjusted_rel_pc = rel_pc;
|
||||
if (map_info != nullptr && adjust_rel_pc) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
|
||||
}
|
||||
frames->resize(num_frames + 1);
|
||||
backtrace_frame_data_t* frame = &frames->at(num_frames);
|
||||
frame->num = num_frames;
|
||||
if (map_info != nullptr) {
|
||||
// This will point to the adjusted absolute pc. regs->pc() is
|
||||
// unaltered.
|
||||
frame->pc = map_info->start + adjusted_rel_pc;
|
||||
} else {
|
||||
frame->pc = rel_pc;
|
||||
}
|
||||
frame->sp = regs->sp();
|
||||
frame->rel_pc = adjusted_rel_pc;
|
||||
frame->stack_size = 0;
|
||||
|
||||
frame->map.start = map_info->start;
|
||||
frame->map.end = map_info->end;
|
||||
frame->map.offset = map_info->offset;
|
||||
frame->map.load_bias = elf->GetLoadBias();
|
||||
frame->map.flags = map_info->flags;
|
||||
frame->map.name = map_info->name;
|
||||
|
||||
uint64_t func_offset = 0;
|
||||
if (!elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
|
||||
frame->func_name = "";
|
||||
}
|
||||
frame->func_offset = func_offset;
|
||||
if (num_frames > 0) {
|
||||
// Set the stack size for the previous frame.
|
||||
backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
|
||||
prev->stack_size = frame->sp - prev->sp;
|
||||
}
|
||||
num_frames++;
|
||||
} else if (!skip_frame && num_ignore_frames > 0) {
|
||||
num_ignore_frames--;
|
||||
}
|
||||
adjust_rel_pc = true;
|
||||
|
||||
// Do not unwind through a device map.
|
||||
if (map_info->flags & PROT_DEVICE_MAP) {
|
||||
break;
|
||||
}
|
||||
unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
|
||||
if (sp_info->flags & PROT_DEVICE_MAP) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!elf->Step(rel_pc + map_info->elf_offset, regs, memory)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktraceCurrent(pid, tid, map), memory_(new unwindstack::MemoryLocal) {}
|
||||
|
||||
std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||
return ::GetFunctionName(Pid(), GetMap(), pc, offset);
|
||||
}
|
||||
|
||||
bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
|
||||
std::unique_ptr<unwindstack::Regs> regs;
|
||||
if (ucontext == nullptr) {
|
||||
regs.reset(unwindstack::Regs::CreateFromLocal());
|
||||
// Fill in the registers from this function. Do it here to avoid
|
||||
// one extra function call appearing in the unwind.
|
||||
unwindstack::RegsGetLocal(regs.get());
|
||||
} else {
|
||||
regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::GetMachineType(), ucontext));
|
||||
}
|
||||
|
||||
error_ = BACKTRACE_UNWIND_NO_ERROR;
|
||||
return ::Unwind(getpid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
|
||||
}
|
||||
|
||||
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
|
||||
: BacktracePtrace(pid, tid, map), memory_(new unwindstack::MemoryRemote(pid)) {}
|
||||
|
||||
std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||
return ::GetFunctionName(Pid(), GetMap(), pc, offset);
|
||||
}
|
||||
|
||||
bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
|
||||
std::unique_ptr<unwindstack::Regs> regs;
|
||||
if (context == nullptr) {
|
||||
uint32_t machine_type;
|
||||
regs.reset(unwindstack::Regs::RemoteGet(Tid(), &machine_type));
|
||||
} else {
|
||||
regs.reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::GetMachineType(), context));
|
||||
}
|
||||
|
||||
error_ = BACKTRACE_UNWIND_NO_ERROR;
|
||||
return ::Unwind(Pid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
|
||||
}
|
||||
|
||||
Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
|
||||
if (pid == BACKTRACE_CURRENT_PROCESS) {
|
||||
pid = getpid();
|
||||
if (tid == BACKTRACE_CURRENT_THREAD) {
|
||||
tid = gettid();
|
||||
}
|
||||
} else if (tid == BACKTRACE_CURRENT_THREAD) {
|
||||
tid = pid;
|
||||
}
|
||||
|
||||
if (map == nullptr) {
|
||||
// This would cause the wrong type of map object to be created, so disallow.
|
||||
#if defined(__ANDROID__)
|
||||
__assert2(__FILE__, __LINE__, __PRETTY_FUNCTION__,
|
||||
"Backtrace::CreateNew() must be called with a real map pointer.");
|
||||
#else
|
||||
BACK_LOGE("Backtrace::CreateNew() must be called with a real map pointer.");
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (pid == getpid()) {
|
||||
return new UnwindStackCurrent(pid, tid, map);
|
||||
} else {
|
||||
return new UnwindStackPtrace(pid, tid, map);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWIND_STACK_H
|
||||
#define _LIBBACKTRACE_UNWIND_STACK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "BacktraceCurrent.h"
|
||||
#include "BacktracePtrace.h"
|
||||
|
||||
class UnwindStackCurrent : public BacktraceCurrent {
|
||||
public:
|
||||
UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map);
|
||||
virtual ~UnwindStackCurrent() = default;
|
||||
|
||||
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
|
||||
|
||||
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<unwindstack::Memory> memory_;
|
||||
};
|
||||
|
||||
class UnwindStackPtrace : public BacktracePtrace {
|
||||
public:
|
||||
UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map);
|
||||
virtual ~UnwindStackPtrace() = default;
|
||||
|
||||
bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
|
||||
|
||||
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
|
||||
|
||||
private:
|
||||
std::unique_ptr<unwindstack::Memory> memory_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWIND_STACK_H
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
#include "UnwindStackMap.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {}
|
||||
|
||||
bool UnwindStackMap::Build() {
|
||||
if (pid_ == 0) {
|
||||
pid_ = getpid();
|
||||
stack_maps_.reset(new unwindstack::LocalMaps);
|
||||
} else {
|
||||
stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
|
||||
}
|
||||
|
||||
if (!stack_maps_->Parse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iterate through the maps and fill in the backtrace_map_t structure.
|
||||
for (auto& map_info : *stack_maps_) {
|
||||
backtrace_map_t map;
|
||||
map.start = map_info.start;
|
||||
map.end = map_info.end;
|
||||
map.offset = map_info.offset;
|
||||
// Set to -1 so that it is demand loaded.
|
||||
map.load_bias = static_cast<uintptr_t>(-1);
|
||||
map.flags = map_info.flags;
|
||||
map.name = map_info.name;
|
||||
|
||||
maps_.push_back(map);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnwindStackMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
|
||||
BacktraceMap::FillIn(addr, map);
|
||||
if (map->load_bias != static_cast<uintptr_t>(-1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill in the load_bias.
|
||||
unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
|
||||
if (map_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
unwindstack::Elf* elf = map_info->GetElf(pid_, true);
|
||||
map->load_bias = elf->GetLoadBias();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// BacktraceMap create function.
|
||||
//-------------------------------------------------------------------------
|
||||
BacktraceMap* BacktraceMap::CreateNew(pid_t pid, bool uncached) {
|
||||
BacktraceMap* map;
|
||||
|
||||
if (uncached) {
|
||||
// Force use of the base class to parse the maps when this call is made.
|
||||
map = new BacktraceMap(pid);
|
||||
} else if (pid == getpid()) {
|
||||
map = new UnwindStackMap(0);
|
||||
} else {
|
||||
map = new UnwindStackMap(pid);
|
||||
}
|
||||
if (!map->Build()) {
|
||||
delete map;
|
||||
return nullptr;
|
||||
}
|
||||
return map;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
#define _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
class UnwindStackMap : public BacktraceMap {
|
||||
public:
|
||||
explicit UnwindStackMap(pid_t pid);
|
||||
~UnwindStackMap() = default;
|
||||
|
||||
bool Build() override;
|
||||
|
||||
void FillIn(uintptr_t addr, backtrace_map_t* map) override;
|
||||
|
||||
unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
|
||||
|
||||
protected:
|
||||
std::unique_ptr<unwindstack::Maps> stack_maps_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
|
@ -90,6 +90,8 @@ public:
|
|||
// If map is NULL, then create the map and manage it internally.
|
||||
// If map is not NULL, the map is still owned by the caller.
|
||||
static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
|
||||
// Same as above, but uses a different underlying unwinder.
|
||||
static Backtrace* CreateNew(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
|
||||
|
||||
// Create an offline Backtrace object that can be used to do an unwind without a process
|
||||
// that is still running. If cache_file is set to true, then elf information will be cached
|
||||
|
|
|
@ -52,6 +52,8 @@ public:
|
|||
// Passing a map created with uncached set to true to Backtrace::Create()
|
||||
// is unsupported.
|
||||
static BacktraceMap* Create(pid_t pid, bool uncached = false);
|
||||
// Same as above, but is compatible with the new unwinder.
|
||||
static BacktraceMap* CreateNew(pid_t pid, bool uncached = false);
|
||||
|
||||
static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ struct MapInfo {
|
|||
uint64_t elf_offset;
|
||||
|
||||
Memory* CreateMemory(pid_t pid);
|
||||
// This function guarantees it will never return nullptr.
|
||||
Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue