Merge "Add support for the new unwind method."

This commit is contained in:
Christopher Ferris 2017-08-01 23:22:42 +00:00 committed by Gerrit Code Review
commit 9ccf627e78
8 changed files with 425 additions and 0 deletions

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
};