diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp index e02aaf216..c58d77733 100644 --- a/libbacktrace/Android.bp +++ b/libbacktrace/Android.bp @@ -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, diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp new file mode 100644 index 000000000..baf0ada28 --- /dev/null +++ b/libbacktrace/UnwindStack.cpp @@ -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 +#include +#include +#include +#include + +#include +#include + +#if !defined(__ANDROID__) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#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(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* frames, + size_t num_ignore_frames) { + unwindstack::Maps* maps = reinterpret_cast(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 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 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); + } +} diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h new file mode 100644 index 000000000..32d1f51ab --- /dev/null +++ b/libbacktrace/UnwindStack.h @@ -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 + +#include + +#include +#include + +#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 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 memory_; +}; + +#endif // _LIBBACKTRACE_UNWIND_STACK_H diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp new file mode 100644 index 000000000..ba9fd87d7 --- /dev/null +++ b/libbacktrace/UnwindStackMap.cpp @@ -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 +#include +#include + +#include +#include +#include +#include + +#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(-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(-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; +} diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h new file mode 100644 index 000000000..7885b7428 --- /dev/null +++ b/libbacktrace/UnwindStackMap.h @@ -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 +#include + +#include +#include + +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 stack_maps_; +}; + +#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h index b919e81c6..d67ea50c9 100644 --- a/libbacktrace/include/backtrace/Backtrace.h +++ b/libbacktrace/include/backtrace/Backtrace.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 diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h index 02a50f738..963c34b64 100644 --- a/libbacktrace/include/backtrace/BacktraceMap.h +++ b/libbacktrace/include/backtrace/BacktraceMap.h @@ -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& maps); diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 78ae40988..b971a9eda 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -34,6 +34,7 @@ cc_defaults { cc_library { name: "libunwindstack", + vendor_available: true, defaults: ["libunwindstack_flags"], export_include_dirs: ["include"], diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h index 2a97dde09..185476799 100644 --- a/libunwindstack/include/unwindstack/MapInfo.h +++ b/libunwindstack/include/unwindstack/MapInfo.h @@ -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); };