diff options
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc')
-rw-r--r-- | toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc new file mode 100644 index 0000000000..4988ef1eb8 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc @@ -0,0 +1,333 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// stackwalker.cc: Generic stackwalker. +// +// See stackwalker.h for documentation. +// +// Author: Mark Mentovai + +#include "google_breakpad/processor/stackwalker.h" + +#include <assert.h> + +#include "common/scoped_ptr.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/dump_context.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/stack_frame_symbolizer.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/linked_ptr.h" +#include "processor/logging.h" +#include "processor/stackwalker_ppc.h" +#include "processor/stackwalker_ppc64.h" +#include "processor/stackwalker_sparc.h" +#include "processor/stackwalker_x86.h" +#include "processor/stackwalker_amd64.h" +#include "processor/stackwalker_arm.h" +#include "processor/stackwalker_arm64.h" +#include "processor/stackwalker_mips.h" + +namespace google_breakpad { + +const int Stackwalker::kRASearchWords = 40; + +// This default is just a sanity check: a large enough value +// that allow capturing unbounded recursion traces, yet provide a +// guardrail against stack walking bugs. The stack walking invariants +// guarantee that the unwinding process is strictly monotonic and +// practically bounded by the size of the stack memory range. +uint32_t Stackwalker::max_frames_ = 1 << 20; // 1M +bool Stackwalker::max_frames_set_ = false; + +uint32_t Stackwalker::max_frames_scanned_ = 1 << 14; // 16k + +Stackwalker::Stackwalker(const SystemInfo* system_info, + MemoryRegion* memory, + const CodeModules* modules, + StackFrameSymbolizer* frame_symbolizer) + : system_info_(system_info), + memory_(memory), + modules_(modules), + unloaded_modules_(NULL), + frame_symbolizer_(frame_symbolizer) { + assert(frame_symbolizer_); +} + +void InsertSpecialAttentionModule( + StackFrameSymbolizer::SymbolizerResult symbolizer_result, + const CodeModule* module, + vector<const CodeModule*>* modules) { + if (!module) { + return; + } + assert(symbolizer_result == StackFrameSymbolizer::kError || + symbolizer_result == StackFrameSymbolizer::kWarningCorruptSymbols); + bool found = false; + vector<const CodeModule*>::iterator iter; + for (iter = modules->begin(); iter != modules->end(); ++iter) { + if (*iter == module) { + found = true; + break; + } + } + if (!found) { + BPLOG(INFO) << ((symbolizer_result == StackFrameSymbolizer::kError) ? + "Couldn't load symbols for: " : + "Detected corrupt symbols for: ") + << module->debug_file() << "|" << module->debug_identifier(); + modules->push_back(module); + } +} + +bool Stackwalker::Walk( + CallStack* stack, + vector<const CodeModule*>* modules_without_symbols, + vector<const CodeModule*>* modules_with_corrupt_symbols) { + BPLOG_IF(ERROR, !stack) << "Stackwalker::Walk requires |stack|"; + assert(stack); + stack->Clear(); + + BPLOG_IF(ERROR, !modules_without_symbols) << "Stackwalker::Walk requires " + << "|modules_without_symbols|"; + BPLOG_IF(ERROR, !modules_without_symbols) << "Stackwalker::Walk requires " + << "|modules_with_corrupt_symbols|"; + assert(modules_without_symbols); + assert(modules_with_corrupt_symbols); + + // Begin with the context frame, and keep getting callers until there are + // no more. + + // Keep track of the number of scanned or otherwise dubious frames seen + // so far, as the caller may have set a limit. + uint32_t scanned_frames = 0; + + // Take ownership of the pointer returned by GetContextFrame. + scoped_ptr<StackFrame> frame(GetContextFrame()); + + while (frame.get()) { + // frame already contains a good frame with properly set instruction and + // frame_pointer fields. The frame structure comes from either the + // context frame (above) or a caller frame (below). + + // Resolve the module information, if a module map was provided. + StackFrameSymbolizer::SymbolizerResult symbolizer_result = + frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_, + system_info_, + frame.get()); + switch (symbolizer_result) { + case StackFrameSymbolizer::kInterrupt: + BPLOG(INFO) << "Stack walk is interrupted."; + return false; + break; + case StackFrameSymbolizer::kError: + InsertSpecialAttentionModule(symbolizer_result, frame->module, + modules_without_symbols); + break; + case StackFrameSymbolizer::kWarningCorruptSymbols: + InsertSpecialAttentionModule(symbolizer_result, frame->module, + modules_with_corrupt_symbols); + break; + case StackFrameSymbolizer::kNoError: + break; + default: + assert(false); + break; + } + + // Keep track of the number of dubious frames so far. + switch (frame.get()->trust) { + case StackFrame::FRAME_TRUST_NONE: + case StackFrame::FRAME_TRUST_SCAN: + case StackFrame::FRAME_TRUST_CFI_SCAN: + scanned_frames++; + break; + default: + break; + } + + // Add the frame to the call stack. Relinquish the ownership claim + // over the frame, because the stack now owns it. + stack->frames_.push_back(frame.release()); + if (stack->frames_.size() > max_frames_) { + // Only emit an error message in the case where the limit + // reached is the default limit, not set by the user. + if (!max_frames_set_) + BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames."; + break; + } + + // Get the next frame and take ownership. + bool stack_scan_allowed = scanned_frames < max_frames_scanned_; + frame.reset(GetCallerFrame(stack, stack_scan_allowed)); + } + + return true; +} + +// static +Stackwalker* Stackwalker::StackwalkerForCPU( + const SystemInfo* system_info, + DumpContext* context, + MemoryRegion* memory, + const CodeModules* modules, + const CodeModules* unloaded_modules, + StackFrameSymbolizer* frame_symbolizer) { + if (!context) { + BPLOG(ERROR) << "Can't choose a stackwalker implementation without context"; + return NULL; + } + + Stackwalker* cpu_stackwalker = NULL; + + uint32_t cpu = context->GetContextCPU(); + switch (cpu) { + case MD_CONTEXT_X86: + cpu_stackwalker = new StackwalkerX86(system_info, + context->GetContextX86(), + memory, modules, frame_symbolizer); + break; + + case MD_CONTEXT_PPC: + cpu_stackwalker = new StackwalkerPPC(system_info, + context->GetContextPPC(), + memory, modules, frame_symbolizer); + break; + + case MD_CONTEXT_PPC64: + cpu_stackwalker = new StackwalkerPPC64(system_info, + context->GetContextPPC64(), + memory, modules, frame_symbolizer); + break; + + case MD_CONTEXT_AMD64: + cpu_stackwalker = new StackwalkerAMD64(system_info, + context->GetContextAMD64(), + memory, modules, frame_symbolizer); + break; + + case MD_CONTEXT_SPARC: + cpu_stackwalker = new StackwalkerSPARC(system_info, + context->GetContextSPARC(), + memory, modules, frame_symbolizer); + break; + + case MD_CONTEXT_MIPS: + case MD_CONTEXT_MIPS64: + cpu_stackwalker = new StackwalkerMIPS(system_info, + context->GetContextMIPS(), + memory, modules, frame_symbolizer); + break; + + case MD_CONTEXT_ARM: + { + int fp_register = -1; + if (system_info->os_short == "ios") + fp_register = MD_CONTEXT_ARM_REG_IOS_FP; + cpu_stackwalker = new StackwalkerARM(system_info, + context->GetContextARM(), + fp_register, memory, modules, + frame_symbolizer); + break; + } + + case MD_CONTEXT_ARM64: + cpu_stackwalker = new StackwalkerARM64(system_info, + context->GetContextARM64(), + memory, modules, + frame_symbolizer); + break; + } + + BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) << + ", can't choose a stackwalker " + "implementation"; + if (cpu_stackwalker) { + cpu_stackwalker->unloaded_modules_ = unloaded_modules; + } + return cpu_stackwalker; +} + +// CONSIDER: check stack alignment? +bool Stackwalker::TerminateWalk(uint64_t caller_ip, + uint64_t caller_sp, + uint64_t callee_sp, + bool first_unwind) const { + // Treat an instruction address less than 4k as end-of-stack. + // (using InstructionAddressSeemsValid() here is very tempting, + // but we need to handle JITted code) + if (caller_ip < (1 << 12)) { + return true; + } + + // NOTE: The stack address range is implicitly checked + // when the stack memory is accessed. + + // The stack pointer should monotonically increase. For first unwind + // we allow caller_sp == callee_sp to account for architectures where + // the return address is stored in a register (so it's possible to have + // leaf functions which don't move the stack pointer) + if (first_unwind ? (caller_sp < callee_sp) : (caller_sp <= callee_sp)) { + return true; + } + + return false; +} + +bool Stackwalker::InstructionAddressSeemsValid(uint64_t address) const { + StackFrame frame; + frame.instruction = address; + StackFrameSymbolizer::SymbolizerResult symbolizer_result = + frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_, + system_info_, &frame); + + if (!frame.module) { + // not inside any loaded module + return false; + } + + if (!frame_symbolizer_->HasImplementation()) { + // No valid implementation to symbolize stack frame, but the address is + // within a known module. + return true; + } + + if (symbolizer_result != StackFrameSymbolizer::kNoError && + symbolizer_result != StackFrameSymbolizer::kWarningCorruptSymbols) { + // Some error occurred during symbolization, but the address is within a + // known module + return true; + } + + return !frame.function_name.empty(); +} + +} // namespace google_breakpad |