michael@0: # HG changeset patch michael@0: # User Julian Seward michael@0: # Date 1372168568 -7200 michael@0: # Tue Jun 25 15:56:08 2013 +0200 michael@0: # Node ID 6d06a09b3f5624dd833bd6f905bfd88e3fdec00a michael@0: # Parent 11f7a9321b7d5d85eddc2db16e58e6870a7c4e06 michael@0: Bug 883126 - Improve performance of EXIDX unwinding in Breakpad. r=ted michael@0: michael@0: diff --git a/src/common/arm_ex_to_module.cc b/src/common/arm_ex_to_module.cc michael@0: --- a/src/common/arm_ex_to_module.cc michael@0: +++ b/src/common/arm_ex_to_module.cc michael@0: @@ -66,141 +66,126 @@ WITH THE SOFTWARE OR THE USE OR OTHER DE michael@0: michael@0: #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) michael@0: #define ARM_EXBUF_COUNT(x) ((x) & 0x0f) michael@0: #define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x)) michael@0: michael@0: using google_breakpad::ustr__pc; michael@0: using google_breakpad::ustr__lr; michael@0: using google_breakpad::ustr__sp; michael@0: +using google_breakpad::ustr__ZDra; michael@0: +using google_breakpad::ustr__ZDcfa; michael@0: using google_breakpad::Module; michael@0: using google_breakpad::ToUniqueString; michael@0: using google_breakpad::UniqueString; michael@0: michael@0: namespace arm_ex_to_module { michael@0: michael@0: // Translate command from extab_data to command for Module. michael@0: int ARMExToModule::TranslateCmd(const struct extab_data* edata, michael@0: - Module::StackFrameEntry* entry, string& vsp) { michael@0: + Module::StackFrameEntry* entry, michael@0: + Module::Expr& vsp) { michael@0: int ret = 0; michael@0: switch (edata->cmd) { michael@0: case ARM_EXIDX_CMD_FINISH: michael@0: /* Copy LR to PC if there isn't currently a rule for PC in force. */ michael@0: if (entry->initial_rules.find(ustr__pc()) michael@0: == entry->initial_rules.end()) { michael@0: if (entry->initial_rules.find(ustr__lr()) michael@0: == entry->initial_rules.end()) { michael@0: - entry->initial_rules[ustr__pc()] = Module::Expr("lr"); michael@0: + entry->initial_rules[ustr__pc()] = Module::Expr(ustr__lr(), michael@0: + 0, false); // "lr" michael@0: } else { michael@0: entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()]; michael@0: } michael@0: } michael@0: break; michael@0: case ARM_EXIDX_CMD_SUB_FROM_VSP: michael@0: - { michael@0: - char c[16]; michael@0: - sprintf(c, " %d -", edata->data); michael@0: - vsp += c; michael@0: - } michael@0: + vsp = vsp.add_delta(- static_cast(edata->data)); michael@0: break; michael@0: case ARM_EXIDX_CMD_ADD_TO_VSP: michael@0: - { michael@0: - char c[16]; michael@0: - sprintf(c, " %d +", edata->data); michael@0: - vsp += c; michael@0: - } michael@0: + vsp = vsp.add_delta(static_cast(edata->data)); michael@0: break; michael@0: case ARM_EXIDX_CMD_REG_POP: michael@0: for (unsigned int i = 0; i < 16; i++) { michael@0: if (edata->data & (1 << i)) { michael@0: - entry->initial_rules[ToUniqueString(regnames[i])] michael@0: - = Module::Expr(vsp + " ^"); michael@0: - vsp += " 4 +"; michael@0: + entry->initial_rules[ToUniqueString(regnames[i])] = vsp.deref(); michael@0: + vsp = vsp.add_delta(4); michael@0: } michael@0: } michael@0: /* Set cfa in case the SP got popped. */ michael@0: if (edata->data & (1 << 13)) { michael@0: - Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()]; michael@0: - // It must be a postfix expression (we don't generate anything michael@0: - // else here), so return -1 to fail out if it isn't. michael@0: - if (!vsp_expr.isExprPostfix()) { michael@0: - ret = -1; michael@0: - break; michael@0: - }; michael@0: - vsp = vsp_expr.getExprPostfix(); michael@0: + vsp = entry->initial_rules[ustr__sp()]; michael@0: } michael@0: break; michael@0: case ARM_EXIDX_CMD_REG_TO_SP: { michael@0: assert (edata->data < 16); michael@0: const char* const regname = regnames[edata->data]; michael@0: const UniqueString* regname_us = ToUniqueString(regname); michael@0: if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) { michael@0: - entry->initial_rules[ustr__sp()] = Module::Expr(regname); michael@0: + entry->initial_rules[ustr__sp()] = Module::Expr(regname_us, michael@0: + 0, false); // "regname" michael@0: } else { michael@0: entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us]; michael@0: } michael@0: - Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()]; michael@0: - if (!vsp_expr.isExprPostfix()) { michael@0: - ret = -1; michael@0: - break; michael@0: - }; michael@0: - vsp = vsp_expr.getExprPostfix(); michael@0: + vsp = entry->initial_rules[ustr__sp()]; michael@0: break; michael@0: } michael@0: case ARM_EXIDX_CMD_VFP_POP: michael@0: /* Don't recover VFP registers, but be sure to adjust the stack michael@0: pointer. */ michael@0: for (unsigned int i = ARM_EXBUF_START(edata->data); michael@0: i <= ARM_EXBUF_END(edata->data); i++) { michael@0: - vsp += " 8 +"; michael@0: + vsp = vsp.add_delta(8); michael@0: } michael@0: if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) { michael@0: - vsp += " 4 +"; michael@0: + vsp = vsp.add_delta(4); michael@0: } michael@0: break; michael@0: case ARM_EXIDX_CMD_WREG_POP: michael@0: for (unsigned int i = ARM_EXBUF_START(edata->data); michael@0: i <= ARM_EXBUF_END(edata->data); i++) { michael@0: - vsp += " 8 +"; michael@0: + vsp = vsp.add_delta(8); michael@0: } michael@0: break; michael@0: case ARM_EXIDX_CMD_WCGR_POP: michael@0: // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4" michael@0: for (unsigned int i = 0; i < 4; i++) { michael@0: if (edata->data & (1 << i)) { michael@0: - vsp += " 4 +"; michael@0: + vsp = vsp.add_delta(4); michael@0: } michael@0: } michael@0: break; michael@0: case ARM_EXIDX_CMD_REFUSED: michael@0: case ARM_EXIDX_CMD_RESERVED: michael@0: ret = -1; michael@0: break; michael@0: } michael@0: return ret; michael@0: } michael@0: michael@0: void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) { michael@0: stack_frame_entry_ = new Module::StackFrameEntry; michael@0: stack_frame_entry_->address = addr; michael@0: stack_frame_entry_->size = size; michael@0: - stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp"); michael@0: - vsp_ = "sp"; michael@0: + Module::Expr sp_expr = Module::Expr(ustr__sp(), 0, false); // "sp" michael@0: + stack_frame_entry_->initial_rules[ustr__ZDcfa()] = sp_expr; // ".cfa" michael@0: + vsp_ = sp_expr; michael@0: } michael@0: michael@0: int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) { michael@0: return TranslateCmd(edata, stack_frame_entry_, vsp_) ; michael@0: } michael@0: michael@0: void ARMExToModule::DeleteStackFrame() { michael@0: delete stack_frame_entry_; michael@0: } michael@0: michael@0: void ARMExToModule::SubmitStackFrame() { michael@0: // return address always winds up in pc michael@0: - stack_frame_entry_->initial_rules[ToUniqueString(kRA)] michael@0: + stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra" michael@0: = stack_frame_entry_->initial_rules[ustr__pc()]; michael@0: // the final value of vsp is the new value of sp michael@0: stack_frame_entry_->initial_rules[ustr__sp()] = vsp_; michael@0: module_->AddStackFrameEntry(stack_frame_entry_); michael@0: } michael@0: michael@0: } // namespace arm_ex_to_module michael@0: diff --git a/src/common/arm_ex_to_module.h b/src/common/arm_ex_to_module.h michael@0: --- a/src/common/arm_ex_to_module.h michael@0: +++ b/src/common/arm_ex_to_module.h michael@0: @@ -89,19 +89,16 @@ struct extab_data { michael@0: uint32_t data; michael@0: }; michael@0: michael@0: enum extab_cmd_flags { michael@0: ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, michael@0: ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX michael@0: }; michael@0: michael@0: -const string kRA = ".ra"; michael@0: -const string kCFA = ".cfa"; michael@0: - michael@0: static const char* const regnames[] = { michael@0: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", michael@0: "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", michael@0: "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", michael@0: "fps", "cpsr" michael@0: }; michael@0: michael@0: // Receives information from arm_ex_reader::ExceptionTableInfo michael@0: @@ -113,17 +110,17 @@ class ARMExToModule { michael@0: ~ARMExToModule() { } michael@0: void AddStackFrame(uintptr_t addr, size_t size); michael@0: int ImproveStackFrame(const struct extab_data* edata); michael@0: void DeleteStackFrame(); michael@0: void SubmitStackFrame(); michael@0: private: michael@0: Module* module_; michael@0: Module::StackFrameEntry* stack_frame_entry_; michael@0: - string vsp_; michael@0: + Module::Expr vsp_; michael@0: int TranslateCmd(const struct extab_data* edata, michael@0: Module::StackFrameEntry* entry, michael@0: - string& vsp); michael@0: + Module::Expr& vsp); michael@0: }; michael@0: michael@0: } // namespace arm_ex_to_module michael@0: michael@0: #endif // COMMON_ARM_EX_TO_MODULE__ michael@0: diff --git a/src/common/module.h b/src/common/module.h michael@0: --- a/src/common/module.h michael@0: +++ b/src/common/module.h michael@0: @@ -39,16 +39,20 @@ michael@0: #define COMMON_LINUX_MODULE_H__ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: +#include michael@0: +#include michael@0: +#include michael@0: + michael@0: #include "common/symbol_data.h" michael@0: #include "common/using_std_string.h" michael@0: #include "common/unique_string.h" michael@0: #include "google_breakpad/common/breakpad_types.h" michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: using std::set; michael@0: @@ -161,30 +165,98 @@ class Module { michael@0: // Construct an invalid expression michael@0: Expr() { michael@0: postfix_ = ""; michael@0: ident_ = NULL; michael@0: offset_ = 0; michael@0: how_ = kExprInvalid; michael@0: } michael@0: bool isExprInvalid() const { return how_ == kExprInvalid; } michael@0: - bool isExprPostfix() const { return how_ == kExprPostfix; } michael@0: michael@0: - // Return the postfix expression string. This is only michael@0: - // meaningful on Exprs for which isExprPostfix returns true. michael@0: - // In all other cases it returns an empty string. michael@0: - string getExprPostfix() const { return postfix_; } michael@0: + // Return the postfix expression string, either directly, michael@0: + // if this is a postfix expression, or by synthesising it michael@0: + // for a simple expression. michael@0: + string getExprPostfix() const { michael@0: + switch (how_) { michael@0: + case kExprPostfix: michael@0: + return postfix_; michael@0: + case kExprSimple: michael@0: + case kExprSimpleMem: { michael@0: + char buf[40]; michael@0: + sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+', michael@0: + how_ == kExprSimple ? "" : " ^"); michael@0: + return string(FromUniqueString(ident_)) + string(buf); michael@0: + } michael@0: + case kExprInvalid: michael@0: + default: michael@0: + assert(0 && "getExprPostfix: invalid Module::Expr type"); michael@0: + return "Expr::genExprPostfix: kExprInvalid"; michael@0: + } michael@0: + } michael@0: michael@0: bool operator==(const Expr& other) const { michael@0: return how_ == other.how_ && michael@0: ident_ == other.ident_ && michael@0: offset_ == other.offset_ && michael@0: postfix_ == other.postfix_; michael@0: } michael@0: michael@0: + // Returns an Expr which evaluates to |this| + |delta| michael@0: + Expr add_delta(long delta) { michael@0: + if (delta == 0) { michael@0: + return *this; michael@0: + } michael@0: + // If it's a simple form expression of the form "identifier + offset", michael@0: + // simply add |delta| on to |offset|. In the other two possible michael@0: + // cases: michael@0: + // *(identifier + offset) michael@0: + // completely arbitrary postfix expression string michael@0: + // the only option is to "downgrade" it to a postfix expression and add michael@0: + // "+/- delta" at the end of the string, since the result can't be michael@0: + // represented in the simple form. michael@0: + switch (how_) { michael@0: + case kExprSimpleMem: michael@0: + case kExprPostfix: { michael@0: + char buf[40]; michael@0: + sprintf(buf, " %ld %c", labs(delta), delta < 0 ? '-' : '+'); michael@0: + return Expr(getExprPostfix() + string(buf)); michael@0: + } michael@0: + case kExprSimple: michael@0: + return Expr(ident_, offset_ + delta, false); michael@0: + case kExprInvalid: michael@0: + default: michael@0: + assert(0 && "add_delta: invalid Module::Expr type"); michael@0: + // Invalid inputs produce an invalid result michael@0: + return Expr(); michael@0: + } michael@0: + } michael@0: + michael@0: + // Returns an Expr which evaluates to *|this| michael@0: + Expr deref() { michael@0: + // In the simplest case, a kExprSimple can be changed into a michael@0: + // kExprSimpleMem. In all other cases it has to be dumped as a michael@0: + // postfix string, and " ^" added at the end. michael@0: + switch (how_) { michael@0: + case kExprSimple: { michael@0: + Expr t = *this; michael@0: + t.how_ = kExprSimpleMem; michael@0: + return t; michael@0: + } michael@0: + case kExprSimpleMem: michael@0: + case kExprPostfix: { michael@0: + return Expr(getExprPostfix() + " ^"); michael@0: + } michael@0: + case kExprInvalid: michael@0: + default: michael@0: + assert(0 && "deref: invalid Module::Expr type"); michael@0: + // Invalid inputs produce an invalid result michael@0: + return Expr(); michael@0: + } michael@0: + } michael@0: + michael@0: // The identifier that gives the starting value for simple expressions. michael@0: const UniqueString* ident_; michael@0: // The offset to add for simple expressions. michael@0: long offset_; michael@0: // The Postfix expression string to evaluate for non-simple expressions. michael@0: string postfix_; michael@0: // The operation expressed by this expression. michael@0: ExprHow how_;