Bug 883126 - Improve performance of EXIDX unwinding in Breakpad. r=ted

This commit is contained in:
Julian Seward 2013-06-25 15:56:08 +02:00
parent 1808439874
commit b58fb25691
3 changed files with 101 additions and 47 deletions

View File

@ -71,6 +71,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
using google_breakpad::ustr__pc;
using google_breakpad::ustr__lr;
using google_breakpad::ustr__sp;
using google_breakpad::ustr__ZDra;
using google_breakpad::ustr__ZDcfa;
using google_breakpad::Module;
using google_breakpad::ToUniqueString;
using google_breakpad::UniqueString;
@ -79,7 +81,8 @@ namespace arm_ex_to_module {
// Translate command from extab_data to command for Module.
int ARMExToModule::TranslateCmd(const struct extab_data* edata,
Module::StackFrameEntry* entry, string& vsp) {
Module::StackFrameEntry* entry,
Module::Expr& vsp) {
int ret = 0;
switch (edata->cmd) {
case ARM_EXIDX_CMD_FINISH:
@ -88,44 +91,29 @@ int ARMExToModule::TranslateCmd(const struct extab_data* edata,
== entry->initial_rules.end()) {
if (entry->initial_rules.find(ustr__lr())
== entry->initial_rules.end()) {
entry->initial_rules[ustr__pc()] = Module::Expr("lr");
entry->initial_rules[ustr__pc()] = Module::Expr(ustr__lr(),
0, false); // "lr"
} else {
entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()];
}
}
break;
case ARM_EXIDX_CMD_SUB_FROM_VSP:
{
char c[16];
sprintf(c, " %d -", edata->data);
vsp += c;
}
vsp = vsp.add_delta(- static_cast<long>(edata->data));
break;
case ARM_EXIDX_CMD_ADD_TO_VSP:
{
char c[16];
sprintf(c, " %d +", edata->data);
vsp += c;
}
vsp = vsp.add_delta(static_cast<long>(edata->data));
break;
case ARM_EXIDX_CMD_REG_POP:
for (unsigned int i = 0; i < 16; i++) {
if (edata->data & (1 << i)) {
entry->initial_rules[ToUniqueString(regnames[i])]
= Module::Expr(vsp + " ^");
vsp += " 4 +";
entry->initial_rules[ToUniqueString(regnames[i])] = vsp.deref();
vsp = vsp.add_delta(4);
}
}
/* Set cfa in case the SP got popped. */
if (edata->data & (1 << 13)) {
Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
// It must be a postfix expression (we don't generate anything
// else here), so return -1 to fail out if it isn't.
if (!vsp_expr.isExprPostfix()) {
ret = -1;
break;
};
vsp = vsp_expr.getExprPostfix();
vsp = entry->initial_rules[ustr__sp()];
}
break;
case ARM_EXIDX_CMD_REG_TO_SP: {
@ -133,16 +121,12 @@ int ARMExToModule::TranslateCmd(const struct extab_data* edata,
const char* const regname = regnames[edata->data];
const UniqueString* regname_us = ToUniqueString(regname);
if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) {
entry->initial_rules[ustr__sp()] = Module::Expr(regname);
entry->initial_rules[ustr__sp()] = Module::Expr(regname_us,
0, false); // "regname"
} else {
entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us];
}
Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()];
if (!vsp_expr.isExprPostfix()) {
ret = -1;
break;
};
vsp = vsp_expr.getExprPostfix();
vsp = entry->initial_rules[ustr__sp()];
break;
}
case ARM_EXIDX_CMD_VFP_POP:
@ -150,23 +134,23 @@ int ARMExToModule::TranslateCmd(const struct extab_data* edata,
pointer. */
for (unsigned int i = ARM_EXBUF_START(edata->data);
i <= ARM_EXBUF_END(edata->data); i++) {
vsp += " 8 +";
vsp = vsp.add_delta(8);
}
if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) {
vsp += " 4 +";
vsp = vsp.add_delta(4);
}
break;
case ARM_EXIDX_CMD_WREG_POP:
for (unsigned int i = ARM_EXBUF_START(edata->data);
i <= ARM_EXBUF_END(edata->data); i++) {
vsp += " 8 +";
vsp = vsp.add_delta(8);
}
break;
case ARM_EXIDX_CMD_WCGR_POP:
// Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4"
for (unsigned int i = 0; i < 4; i++) {
if (edata->data & (1 << i)) {
vsp += " 4 +";
vsp = vsp.add_delta(4);
}
}
break;
@ -182,8 +166,9 @@ void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) {
stack_frame_entry_ = new Module::StackFrameEntry;
stack_frame_entry_->address = addr;
stack_frame_entry_->size = size;
stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp");
vsp_ = "sp";
Module::Expr sp_expr = Module::Expr(ustr__sp(), 0, false); // "sp"
stack_frame_entry_->initial_rules[ustr__ZDcfa()] = sp_expr; // ".cfa"
vsp_ = sp_expr;
}
int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) {
@ -196,7 +181,7 @@ void ARMExToModule::DeleteStackFrame() {
void ARMExToModule::SubmitStackFrame() {
// return address always winds up in pc
stack_frame_entry_->initial_rules[ToUniqueString(kRA)]
stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra"
= stack_frame_entry_->initial_rules[ustr__pc()];
// the final value of vsp is the new value of sp
stack_frame_entry_->initial_rules[ustr__sp()] = vsp_;

View File

@ -94,9 +94,6 @@ enum extab_cmd_flags {
ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX
};
const string kRA = ".ra";
const string kCFA = ".cfa";
static const char* const regnames[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
@ -118,10 +115,10 @@ class ARMExToModule {
private:
Module* module_;
Module::StackFrameEntry* stack_frame_entry_;
string vsp_;
Module::Expr vsp_;
int TranslateCmd(const struct extab_data* edata,
Module::StackFrameEntry* entry,
string& vsp);
Module::Expr& vsp);
};
} // namespace arm_ex_to_module

View File

@ -44,6 +44,10 @@
#include <string>
#include <vector>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "common/symbol_data.h"
#include "common/using_std_string.h"
#include "common/unique_string.h"
@ -166,12 +170,27 @@ class Module {
how_ = kExprInvalid;
}
bool isExprInvalid() const { return how_ == kExprInvalid; }
bool isExprPostfix() const { return how_ == kExprPostfix; }
// Return the postfix expression string. This is only
// meaningful on Exprs for which isExprPostfix returns true.
// In all other cases it returns an empty string.
string getExprPostfix() const { return postfix_; }
// Return the postfix expression string, either directly,
// if this is a postfix expression, or by synthesising it
// for a simple expression.
string getExprPostfix() const {
switch (how_) {
case kExprPostfix:
return postfix_;
case kExprSimple:
case kExprSimpleMem: {
char buf[40];
sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+',
how_ == kExprSimple ? "" : " ^");
return string(FromUniqueString(ident_)) + string(buf);
}
case kExprInvalid:
default:
assert(0 && "getExprPostfix: invalid Module::Expr type");
return "Expr::genExprPostfix: kExprInvalid";
}
}
bool operator==(const Expr& other) const {
return how_ == other.how_ &&
@ -180,6 +199,59 @@ class Module {
postfix_ == other.postfix_;
}
// Returns an Expr which evaluates to |this| + |delta|
Expr add_delta(long delta) {
if (delta == 0) {
return *this;
}
// If it's a simple form expression of the form "identifier + offset",
// simply add |delta| on to |offset|. In the other two possible
// cases:
// *(identifier + offset)
// completely arbitrary postfix expression string
// the only option is to "downgrade" it to a postfix expression and add
// "+/- delta" at the end of the string, since the result can't be
// represented in the simple form.
switch (how_) {
case kExprSimpleMem:
case kExprPostfix: {
char buf[40];
sprintf(buf, " %ld %c", labs(delta), delta < 0 ? '-' : '+');
return Expr(getExprPostfix() + string(buf));
}
case kExprSimple:
return Expr(ident_, offset_ + delta, false);
case kExprInvalid:
default:
assert(0 && "add_delta: invalid Module::Expr type");
// Invalid inputs produce an invalid result
return Expr();
}
}
// Returns an Expr which evaluates to *|this|
Expr deref() {
// In the simplest case, a kExprSimple can be changed into a
// kExprSimpleMem. In all other cases it has to be dumped as a
// postfix string, and " ^" added at the end.
switch (how_) {
case kExprSimple: {
Expr t = *this;
t.how_ = kExprSimpleMem;
return t;
}
case kExprSimpleMem:
case kExprPostfix: {
return Expr(getExprPostfix() + " ^");
}
case kExprInvalid:
default:
assert(0 && "deref: invalid Module::Expr type");
// Invalid inputs produce an invalid result
return Expr();
}
}
// The identifier that gives the starting value for simple expressions.
const UniqueString* ident_;
// The offset to add for simple expressions.