/* * 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 "backtrace_helper.h" #if defined(__linux__) #include #include #include #include "unwindstack/Regs.h" #include "unwindstack/RegsGetLocal.h" #include "unwindstack/Memory.h" #include "unwindstack/Unwinder.h" #include "base/bit_utils.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "thread-inl.h" #else // For UNUSED #include "base/macros.h" #endif namespace art { // We only really support libunwindstack on linux which is unfortunate but since this is only for // gcstress this isn't a huge deal. #if defined(__linux__) // Strict integrity check of the backtrace: // All methods must have a name, all the way to "main". static constexpr bool kStrictUnwindChecks = true; struct UnwindHelper : public TLSData { static constexpr const char* kTlsKey = "UnwindHelper::kTlsKey"; explicit UnwindHelper(size_t max_depth) : arch_(unwindstack::Regs::CurrentArch()), memory_(unwindstack::Memory::CreateProcessMemoryThreadCached(getpid())), jit_(unwindstack::CreateJitDebug(arch_, memory_)), dex_(unwindstack::CreateDexFiles(arch_, memory_)), unwinder_(max_depth, &maps_, memory_) { CHECK(maps_.Parse()); unwinder_.SetArch(arch_); unwinder_.SetJitDebug(jit_.get()); unwinder_.SetDexFiles(dex_.get()); unwinder_.SetResolveNames(kStrictUnwindChecks); unwindstack::Elf::SetCachingEnabled(true); } // Reparse process mmaps to detect newly loaded libraries. bool Reparse(bool* any_changed) { return maps_.Reparse(any_changed); } static UnwindHelper* Get(Thread* self, size_t max_depth) { UnwindHelper* tls = reinterpret_cast(self->GetCustomTLS(kTlsKey)); if (tls == nullptr) { tls = new UnwindHelper(max_depth); self->SetCustomTLS(kTlsKey, tls); } return tls; } unwindstack::Unwinder* Unwinder() { return &unwinder_; } private: unwindstack::LocalUpdatableMaps maps_; unwindstack::ArchEnum arch_; std::shared_ptr memory_; std::unique_ptr jit_; std::unique_ptr dex_; unwindstack::Unwinder unwinder_; }; void BacktraceCollector::Collect() { unwindstack::Unwinder* unwinder = UnwindHelper::Get(Thread::Current(), max_depth_)->Unwinder(); if (!CollectImpl(unwinder)) { // Reparse process mmaps to detect newly loaded libraries and retry, // but only if any maps changed (we don't want to hide racy failures). bool any_changed; UnwindHelper::Get(Thread::Current(), max_depth_)->Reparse(&any_changed); if (!any_changed || !CollectImpl(unwinder)) { if (kStrictUnwindChecks) { std::vector& frames = unwinder->frames(); LOG(ERROR) << "Failed to unwind stack (error " << unwinder->LastErrorCodeString() << "):"; for (auto it = frames.begin(); it != frames.end(); it++) { if (it == frames.begin() || std::prev(it)->map_name != it->map_name) { LOG(ERROR) << " in " << it->map_name.c_str(); } LOG(ERROR) << " pc " << std::setw(8) << std::setfill('0') << std::hex << it->rel_pc << " " << it->function_name.c_str(); } LOG(FATAL); } } } } bool BacktraceCollector::CollectImpl(unwindstack::Unwinder* unwinder) { std::unique_ptr regs(unwindstack::Regs::CreateFromLocal()); RegsGetLocal(regs.get()); unwinder->SetRegs(regs.get()); unwinder->Unwind(); num_frames_ = 0; if (unwinder->NumFrames() > skip_count_) { for (auto it = unwinder->frames().begin() + skip_count_; it != unwinder->frames().end(); ++it) { CHECK_LT(num_frames_, max_depth_); out_frames_[num_frames_++] = static_cast(it->pc); // Expected early end: Instrumentation breaks unwinding (b/138296821). // Inexact compare because the unwinder does not give us exact return address, // but rather it tries to guess the address of the preceding call instruction. size_t exit_pc = reinterpret_cast(GetQuickInstrumentationExitPc()); if (exit_pc - 4 <= it->pc && it->pc <= exit_pc) { return true; } if (kStrictUnwindChecks) { if (it->function_name.empty()) { return false; } if (it->function_name == "main" || it->function_name == "start_thread" || it->function_name == "__start_thread") { return true; } } } } unwindstack::ErrorCode error = unwinder->LastErrorCode(); return error == unwindstack::ERROR_NONE || error == unwindstack::ERROR_MAX_FRAMES_EXCEEDED; } #else #pragma clang diagnostic push #pragma clang diagnostic warning "-W#warnings" #warning "Backtrace collector is not implemented. GCStress cannot be used." #pragma clang diagnostic pop // We only have an implementation for linux. On other plaforms just return nothing. This is not // really correct but we only use this for hashing and gcstress so it's not too big a deal. void BacktraceCollector::Collect() { UNUSED(skip_count_); UNUSED(out_frames_); UNUSED(max_depth_); num_frames_ = 0; } #endif } // namespace art