Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1)

Unified Diff: src/processor/stackwalker_MIPS.cc

Issue 469002: MIPS support for Stackwalker module Base URL: http://google-breakpad.googlecode.com/svn/trunk/
Patch Set: Created 12 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/processor/stackwalker_MIPS.cc
===================================================================
--- src/processor/stackwalker_MIPS.cc (revision 0)
+++ src/processor/stackwalker_MIPS.cc (revision 0)
@@ -0,0 +1,490 @@
+// Copyright (c) 2010 Google Inc.
Ted Mielczarek 2012/09/28 12:44:08 1) The filename of this (and the matching header f
+// 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_MIPS.cc: MIPS-specific stackwalker.
+//
+// See stackwalker_MIPS.h for documentation.
+//
+// Author: Tata Elxsi
Ted Mielczarek 2012/09/28 12:44:08 Do you have an email address for this person? It's
+
+#include "processor/postfix_evaluator-inl.h"
Ted Mielczarek 2012/09/28 12:44:08 Include lines should be ordered by filename unles
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/memory_region.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/logging.h"
+#include "processor/scoped_ptr.h"
+#include "processor/stackwalker_x86.h"
+#include "processor/windows_frame_info.h"
+#include "processor/cfi_frame_info.h"
+#include "processor/stackwalker_MIPS.h"
+#include "google_breakpad/common/minidump_cpu_mips.h"
Ted Mielczarek 2012/09/28 12:44:08 This include shouldn't be necessary.
+
+using std::hex;
+using std::dec;
+namespace google_breakpad {
+
+
+StackwalkerMIPS::StackwalkerMIPS (const SystemInfo *system_info,
Ted Mielczarek 2012/09/28 12:44:08 Here (and elsewhere), the * should be next to the
+ const MDRawContextMIPS *context,
+ MemoryRegion *memory,
+ const CodeModules *modules,
+ SymbolSupplier *supplier,
+ SourceLineResolverInterface *resolver)
+ : Stackwalker(system_info, memory, modules, supplier, resolver),
+ context_(context) {
+ if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
+ BPLOG(ERROR) << "Memory out of range for stackwalking: " <<
+ HexString(memory_->GetBase()) << "+" <<
+ HexString(memory_->GetSize());
+ memory_ = NULL;
Ted Mielczarek 2012/09/28 12:44:08 You're asserting that the stack addresses will alw
+ }
+}
+
+StackFrameMIPS::~StackFrameMIPS() {
+ if (cfi_frame_info)
+ delete cfi_frame_info;
+ cfi_frame_info = NULL;
+}
+
+StackFrame *StackwalkerMIPS::GetContextFrame() {
+ if (!context_ || !memory_) {
+ BPLOG(ERROR) << "Can't get context frame without context or memory";
+ return NULL;
+ }
+
+ StackFrameMIPS *frame = new StackFrameMIPS();
+
+ // The instruction pointer is stored directly in a register, so pull it
+ // straight out of the CPU context structure.
+ frame->context = *context_;
+ frame->context_validity = StackFrameMIPS::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.epc;
+
+ return frame;
+}
+
+// Register names for mips
+
+static const char *register_names[] = {
digit 2013/03/21 08:28:43 Please use: static const char* const register_n
+ "$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", "$to", "$t1",
+ "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$s0", "$s1", "$s2", "$s3",
+ "$s4", "$s5", "$s6", "$s7", "$t8", "$t9", "$k0", "$k1", "$gp", "$sp",
+ "$fp", "$ra", NULL
+};
+
+// Return address register $ra is updated by the processor on calling
+// JAL instruction [ Jump and link ]. It is the responsiblity of a
+// subroutine to save it's return address before calling any other
+// subroutine. To get the pc of the caller, $ra at the start of
+// current subroutine is requied. To find this parsing and handling
+// of CFI of current frame is required. On doing this, we will get all
Ted Mielczarek 2012/09/28 12:44:08 Don't use "we" in comments.
+// the registers except $ra updated to the values at the point
+// when current subroutine is invoked.
+// We can use the current $ra to find the $pc of the caller.
+// And then process the CFI of caller frame to find $ra at beginning
+// of the caller function. Frame obtained in previous step and
+// current $ra gives the caller frame.
+// Thus processing of two CFI frames are required to find caller frame
+// in case of MIPS.
+
+StackFrameMIPS *StackwalkerMIPS::GetCallerByCFIFrameInfo (
Ted Mielczarek 2012/09/28 12:44:08 I don't have the mental energy to review the actua
+ const vector<StackFrame *> &frames,
+ CFIFrameInfo *cfi_frame_info) {
+ StackFrameMIPS *last_frame = static_cast<StackFrameMIPS *>(frames.back());
+
+ unsigned long sp = 0, pc = 0;
+
+ // Populate a dictionary with the valid register values in last_frame.
+ CFIFrameInfo::RegisterValueMap<u_int32_t> callee_registers;
+ // Use the STACK CFI data to recover the caller's register values.
+ CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
+ // Register map to save callee frame map at beginning of the frame.
+ CFIFrameInfo::RegisterValueMap<u_int32_t> callee_registers_start;
+
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << "Entering "<< __FUNCTION__ << " Populating previous frame";
+ #endif
Ted Mielczarek 2012/09/28 12:44:08 These debug messages don't seem terribly valuable
+
+ for (int i = 0; register_names[i]; i++) {
+ callee_registers_start[register_names[i]] = last_frame->context.iregs[i];
+ callee_registers[register_names[i]] = last_frame->context.iregs[i];
+ }
+
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<" Populating previous frame complete." ;
+ #endif
+
+ if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
+ &callee_registers_start)) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " FindCallerRegs failed for callee_registers_start";
+ #endif
+
+ return NULL;
+ }
+
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry1 =
+ callee_registers_start.find(".cfa");
+
+ if (entry1 != callee_registers_start.end()) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<"After FindCallerRegs:cfa = "
+ <<hex<<entry1->second ;
+ #endif
+ sp = entry1->second;
+ callee_registers_start["$sp"] = entry1->second;
+ }
+
+ entry1 = callee_registers_start.find(".ra");
+ if (entry1 != callee_registers_start.end()) {
+
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<"After FindCallerRegs:ra = "
+ <<hex<<entry1->second ;
+ #endif
+
+ callee_registers_start["$ra"] = entry1->second;
+ pc = entry1->second - 8;
+ }
+
+ callee_registers_start["$pc"] = pc;
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<" Callee frame start populated cfa = "
+ <<hex<<callee_registers_start[".cfa"]<<" $ra = "
+ << callee_registers_start["$ra"]
+ << "$pc = " <<callee_registers_start["$pc"]<< "$sp = "
+ <<callee_registers_start["$sp"]<<dec;
+ #endif
+
+ StackFrameMIPS * new_frame = new StackFrameMIPS(*last_frame);
+
+ // If the resolver has DWARF CFI information, use that.
+
+ for (int i = 0; register_names[i]; i++) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
+ callee_registers_start.find(register_names[i]);
+ if (entry != caller_registers.end()) {
+ // We recovered the value of this register; fill the context with the
+ // value from caller_registers.
+ new_frame->context.iregs[i] = entry->second;
+ }
+ else {
+ callee_registers_start[register_names[i]] =
+ callee_registers[register_names[i]];
+ new_frame->context.iregs[i] = last_frame->context.iregs[i] ;
+ }
+ }
+
+ new_frame->context.epc = callee_registers_start["$pc"];
+ new_frame->instruction = callee_registers_start["$pc"];
+
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<hex<<" Calling FindCFIFrameInfo, sp : "
+ << callee_registers_start["$sp"] << "$zero = "
+ <<callee_registers_start["$zero"] <<dec;
+ #endif
+
+ CFIFrameInfo *cfi_frame_info_new =
+ resolver_ ? resolver_->FindCFIFrameInfo(new_frame) : NULL;
+
+ if (cfi_frame_info_new) {
+ if (!cfi_frame_info_new->FindCallerRegs(callee_registers_start,
+ *memory_,&caller_registers)) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " FindCallerRegs failed Returning NULL ";
+ #endif
+
+ return NULL;
+ }
+ }
+
+ else {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " cfi_frame_info_new NULL ";
+ #endif
+ if (!resolver_)
+ BPLOG(INFO) << " resolver NULL ";
+ }
+
+ entry1 = callee_registers_start.find(".ra");
+ if (entry1 != callee_registers_start.end()) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<" After FindCallerRegs,ra = "
+ <<hex<<entry1->second ;
+ #endif
+ callee_registers_start["$ra"] = entry1->second;
+ }
+
+ // Construct a new stack frame given the values the CFI recovered.
+ scoped_ptr<StackFrameMIPS> frame(new StackFrameMIPS());
+
+ for (int i = 0; register_names[i]; i++) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
+ callee_registers_start.find(register_names[i]);
+ if (entry != callee_registers_start.end()) {
+ // We recovered the value of this register; fill the context with the
+ // value from caller_registers.
+ // frame->context_validity |= StackFrameMIPS::RegisterValidFlag(i);
+ frame->context.iregs[i] = entry->second;
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " Entry recovered "<<register_names[i] << " val "
+ << hex << frame->context.iregs[i] <<dec;
+ #endif
+ } else if (0 <= i && i <= 31 ) {
+ // If the STACK CFI data doesn't mention some callee-saves register, and
+ // it is valid in the callee, assume the callee has not yet changed it.
+ // Registers r4 through r11 are callee-saves, according to the Procedure
+ // Call Standard for the ARM Architecture, which the Linux ABI follows.
+ //frame->context_validity |= StackFrameMIPS::RegisterValidFlag(i);
+
+ frame->context.iregs[i] = last_frame->context.iregs[i];
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " Entry copied "<<register_names[i] << " val "
+ <<hex << frame->context.iregs[i] << "\tindex " <<dec << i;
+ #endif
+ }
+ }
+
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
+ caller_registers.find(".cfa");
+ if (entry != caller_registers.end()) {
+ BPLOG(INFO) << " .cfa : " <<hex << entry->second;
+ }
+ else {
+ BPLOG(INFO) << " .cfa not found: " ;
+ }
+ entry = caller_registers.find(".ra");
+ if (entry != caller_registers.end()) {
+ BPLOG(INFO) << " .ra : " << entry->second;
+ }
+ else {
+ BPLOG(INFO) << " .ra not found: " ;
+ }
+ entry = callee_registers_start.find("$pc");
+ if (entry != callee_registers_start.end()) {
+ frame->context.epc = entry->second;
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " .epc : " << entry->second;
+ #endif
+ }
+ entry = caller_registers.find("$ra");
+ if (entry == caller_registers.end())
+ {
+ frame->context.iregs[31/*RA_INDEX*/] = entry->second;
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << "ra copied : " << entry->second;
+ #endif
+ }
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << "Returning frame pc = 0x"<< caller_registers["$pc"] <<dec;
+ #endif
+
+ return frame.release();
+}
+
+StackFrame *StackwalkerMIPS::GetCallerFrame(const CallStack *stack) {
+ if (!memory_ || !stack) {
+ BPLOG(ERROR) << "Can't get caller frame without memory or stack";
+ return NULL;
+ }
+
+ const vector<StackFrame *> &frames = *stack->frames();
+ StackFrameMIPS *last_frame = static_cast<StackFrameMIPS *>(frames.back());
+ scoped_ptr<StackFrameMIPS> new_frame;
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " Entering " << __FUNCTION__;
+ #endif
+ // If the resolver has DWARF CFI information, use that.
+ if (!new_frame.get()) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<"Calling FindCFIFrameInfo for last frame";
+ #endif
+ CFIFrameInfo *cfi_frame_info =
+ resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL;
+ if (cfi_frame_info) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << __FUNCTION__ <<" FindCFIFrameInfo completed" ;
+ #endif
+ new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info));
+ }
+ }
+
+ if (!new_frame.get()) {
+ #ifdef SW_DEBUG
+ printf("Entering GetCallerByFPAtBase %s %d\n",__FILE__,__LINE__);
+ #endif
+ new_frame.reset(GetCallerByFPAtBase(frames));
+ }
+ // If nothing worked, tell the caller.
+ if (!new_frame.get()) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " Returning NULL ";
+ #endif
+ return NULL;
+ }
+
+ // Treat an instruction address of 0 as end-of-stack.
+ if (new_frame->context.epc== 0) {
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " EPC 0 Returning NULL ";
+ #endif
+ return NULL;
+ }
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+
+ if (new_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP]<=
+ last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP]){
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " Stack pointer issue old " << hex <<
+ last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP]<<" new "
+ <<new_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP]<<dec<<
+ "Returning NULL ";
+ #endif
+ }
+
+ // new_frame->context.epc is the return address, which is one instruction
+ // past the CALL that caused us to arrive at the callee. Set
+ // new_frame->instruction to one less than that. This won't reference the
+ // beginning of the CALL instruction, but it's guaranteed to be within
+ // the CALL, which is sufficient to get the source line information to
+ // match up with the line that contains a function call. Callers that
+ // require the exact return address value may access the context.epc
+ // field of StackFrameMIPS.
+ new_frame->instruction = new_frame->context.epc;
+
+ return new_frame.release();
+}
+
+ // In the prologue of mips, Frame Pointer is updated only after stacksize
+ // is updated. $ra can be found by content of address ($s8+variable_offest)
+ // So we cannot obtain $ra by dereferencing frame pointer + a fixed offset.
+ // Previous implementation was using frame pointer to get $ra
+ // which resulted in junk frames.
+
+#define MAX_FRAME_STACK_SIZE 1024
+StackFrameMIPS *StackwalkerMIPS::GetCallerByFPAtBase(
+ const vector<StackFrame *> &frames) {
+ StackFrame::FrameTrust trust;
+ StackFrameMIPS *last_frame = static_cast<StackFrameMIPS *>(frames.back());
+ u_int32_t last_esp = last_frame->context.iregs[MD_CONTEXT_MIPS_REG_SP];
+ u_int32_t last_efp = last_frame->context.iregs[MD_CONTEXT_MIPS_REG_FP];
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << " Entering GetCallerByFPAtBase";
+ #endif
+
+ u_int32_t caller_epc, caller_esp, caller_efp;
+ // Return address pc cannot be obtained directly.
+ // So commenting this and forcing stackwalking
+#if 0
+ if (memory_->GetMemoryAtAddress(last_efp + 4, &caller_epc) &&
+ memory_->GetMemoryAtAddress(last_efp, &caller_efp)) {
+ caller_esp = last_efp + 8;
+ trust = StackFrame::FRAME_TRUST_FP;
+ } else
+#endif
+ {
+ // We cannot use frame pointer to get the return address.
+ // We'll scan the stack for a
+ // return address. This can happen if last_frame is executing code
+ // for a module for which we don't have symbols.
+ int count = MAX_FRAME_STACK_SIZE/sizeof(caller_epc);
+ do
+ {
+ /*Scanning for return address from stack pointer of last frame*/
+ if (!ScanForReturnAddress(last_esp, &caller_esp, &caller_epc, count)) {
+ // if we can't find an instruction pointer even with stack scanning,
+ // give up.
+ BPLOG(ERROR) << " ScanForReturnAddress failed " ;
+ return NULL;
+ }
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << "count "<<count<< std::hex<<" last_esp: "
+ <<last_esp<< " caller_esp: " <<caller_esp<<" caller_epc: "
+ <<caller_epc<< std::dec;
+ if (caller_esp == last_esp) {
+ BPLOG(INFO) << " Retrieved caller_esp and last_esp are same." ;
+ }
+ #endif
+ if (!memory_->GetMemoryAtAddress(caller_esp-4, &caller_efp))
+ {
+ BPLOG(INFO) << " GetMemoryAtAddress failed " ;
+ return NULL;
+ }
+ #ifdef SW_DEBUG
+ BPLOG(INFO) << std::hex<< " caller_efp: " <<caller_efp<< std::dec;
+ #endif
+
+ count = count - ( caller_esp - last_esp)/sizeof(caller_epc);
+ /* Now scan the next address in the stack*/
+ last_esp = caller_esp+sizeof(caller_epc);
+ }while (caller_efp - caller_esp >= MAX_FRAME_STACK_SIZE && count > 0);
+
+ if (!count)
+ {
+ BPLOG(INFO) << " No frame found " ;
+ return NULL;
+ }
+
+ // ScanForReturnAddress found a reasonable return address. Advance
+ // %esp to the location above the one where the return address was
+ // found. Assume that %ebp is unchanged.
+ caller_esp += 4;
+ caller_efp = last_efp;
+ trust = StackFrame::FRAME_TRUST_SCAN;
+ }
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameMIPS *frame = new StackFrameMIPS();
+ frame->trust = trust;
+ frame->context = last_frame->context;
+ frame->context.epc = caller_epc;
+ frame->instruction = caller_epc;
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_SP] = caller_esp;
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_FP] = caller_efp;
+ frame->context.iregs[MD_CONTEXT_MIPS_REG_RA] = caller_epc;
+ #ifdef SW_DEBUG
+ for (int i = 0; register_names[i]; i++) {
+ BPLOG(INFO) << register_names[i] << " :0x"
+ <<hex << frame->context.iregs[i]<<dec;
+ }
+ #endif
+
+ return frame;
+ }
+
+
+} // namespace google_breakpad
Property changes on: src/processor/stackwalker_MIPS.cc
___________________________________________________________________
Added: svn:executable
+ *

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld 1004:630ec63f810e-tainted