|
1 # HG changeset patch |
|
2 # User Julian Seward <jseward@acm.org> |
|
3 # Date 1372168568 -7200 |
|
4 # Tue Jun 25 15:56:08 2013 +0200 |
|
5 # Node ID 6d06a09b3f5624dd833bd6f905bfd88e3fdec00a |
|
6 # Parent 11f7a9321b7d5d85eddc2db16e58e6870a7c4e06 |
|
7 Bug 883126 - Improve performance of EXIDX unwinding in Breakpad. r=ted |
|
8 |
|
9 diff --git a/src/common/arm_ex_to_module.cc b/src/common/arm_ex_to_module.cc |
|
10 --- a/src/common/arm_ex_to_module.cc |
|
11 +++ b/src/common/arm_ex_to_module.cc |
|
12 @@ -66,141 +66,126 @@ WITH THE SOFTWARE OR THE USE OR OTHER DE |
|
13 |
|
14 #define ARM_EXBUF_START(x) (((x) >> 4) & 0x0f) |
|
15 #define ARM_EXBUF_COUNT(x) ((x) & 0x0f) |
|
16 #define ARM_EXBUF_END(x) (ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x)) |
|
17 |
|
18 using google_breakpad::ustr__pc; |
|
19 using google_breakpad::ustr__lr; |
|
20 using google_breakpad::ustr__sp; |
|
21 +using google_breakpad::ustr__ZDra; |
|
22 +using google_breakpad::ustr__ZDcfa; |
|
23 using google_breakpad::Module; |
|
24 using google_breakpad::ToUniqueString; |
|
25 using google_breakpad::UniqueString; |
|
26 |
|
27 namespace arm_ex_to_module { |
|
28 |
|
29 // Translate command from extab_data to command for Module. |
|
30 int ARMExToModule::TranslateCmd(const struct extab_data* edata, |
|
31 - Module::StackFrameEntry* entry, string& vsp) { |
|
32 + Module::StackFrameEntry* entry, |
|
33 + Module::Expr& vsp) { |
|
34 int ret = 0; |
|
35 switch (edata->cmd) { |
|
36 case ARM_EXIDX_CMD_FINISH: |
|
37 /* Copy LR to PC if there isn't currently a rule for PC in force. */ |
|
38 if (entry->initial_rules.find(ustr__pc()) |
|
39 == entry->initial_rules.end()) { |
|
40 if (entry->initial_rules.find(ustr__lr()) |
|
41 == entry->initial_rules.end()) { |
|
42 - entry->initial_rules[ustr__pc()] = Module::Expr("lr"); |
|
43 + entry->initial_rules[ustr__pc()] = Module::Expr(ustr__lr(), |
|
44 + 0, false); // "lr" |
|
45 } else { |
|
46 entry->initial_rules[ustr__pc()] = entry->initial_rules[ustr__lr()]; |
|
47 } |
|
48 } |
|
49 break; |
|
50 case ARM_EXIDX_CMD_SUB_FROM_VSP: |
|
51 - { |
|
52 - char c[16]; |
|
53 - sprintf(c, " %d -", edata->data); |
|
54 - vsp += c; |
|
55 - } |
|
56 + vsp = vsp.add_delta(- static_cast<long>(edata->data)); |
|
57 break; |
|
58 case ARM_EXIDX_CMD_ADD_TO_VSP: |
|
59 - { |
|
60 - char c[16]; |
|
61 - sprintf(c, " %d +", edata->data); |
|
62 - vsp += c; |
|
63 - } |
|
64 + vsp = vsp.add_delta(static_cast<long>(edata->data)); |
|
65 break; |
|
66 case ARM_EXIDX_CMD_REG_POP: |
|
67 for (unsigned int i = 0; i < 16; i++) { |
|
68 if (edata->data & (1 << i)) { |
|
69 - entry->initial_rules[ToUniqueString(regnames[i])] |
|
70 - = Module::Expr(vsp + " ^"); |
|
71 - vsp += " 4 +"; |
|
72 + entry->initial_rules[ToUniqueString(regnames[i])] = vsp.deref(); |
|
73 + vsp = vsp.add_delta(4); |
|
74 } |
|
75 } |
|
76 /* Set cfa in case the SP got popped. */ |
|
77 if (edata->data & (1 << 13)) { |
|
78 - Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()]; |
|
79 - // It must be a postfix expression (we don't generate anything |
|
80 - // else here), so return -1 to fail out if it isn't. |
|
81 - if (!vsp_expr.isExprPostfix()) { |
|
82 - ret = -1; |
|
83 - break; |
|
84 - }; |
|
85 - vsp = vsp_expr.getExprPostfix(); |
|
86 + vsp = entry->initial_rules[ustr__sp()]; |
|
87 } |
|
88 break; |
|
89 case ARM_EXIDX_CMD_REG_TO_SP: { |
|
90 assert (edata->data < 16); |
|
91 const char* const regname = regnames[edata->data]; |
|
92 const UniqueString* regname_us = ToUniqueString(regname); |
|
93 if (entry->initial_rules.find(regname_us) == entry->initial_rules.end()) { |
|
94 - entry->initial_rules[ustr__sp()] = Module::Expr(regname); |
|
95 + entry->initial_rules[ustr__sp()] = Module::Expr(regname_us, |
|
96 + 0, false); // "regname" |
|
97 } else { |
|
98 entry->initial_rules[ustr__sp()] = entry->initial_rules[regname_us]; |
|
99 } |
|
100 - Module::Expr& vsp_expr = entry->initial_rules[ustr__sp()]; |
|
101 - if (!vsp_expr.isExprPostfix()) { |
|
102 - ret = -1; |
|
103 - break; |
|
104 - }; |
|
105 - vsp = vsp_expr.getExprPostfix(); |
|
106 + vsp = entry->initial_rules[ustr__sp()]; |
|
107 break; |
|
108 } |
|
109 case ARM_EXIDX_CMD_VFP_POP: |
|
110 /* Don't recover VFP registers, but be sure to adjust the stack |
|
111 pointer. */ |
|
112 for (unsigned int i = ARM_EXBUF_START(edata->data); |
|
113 i <= ARM_EXBUF_END(edata->data); i++) { |
|
114 - vsp += " 8 +"; |
|
115 + vsp = vsp.add_delta(8); |
|
116 } |
|
117 if (!(edata->data & ARM_EXIDX_VFP_FSTMD)) { |
|
118 - vsp += " 4 +"; |
|
119 + vsp = vsp.add_delta(4); |
|
120 } |
|
121 break; |
|
122 case ARM_EXIDX_CMD_WREG_POP: |
|
123 for (unsigned int i = ARM_EXBUF_START(edata->data); |
|
124 i <= ARM_EXBUF_END(edata->data); i++) { |
|
125 - vsp += " 8 +"; |
|
126 + vsp = vsp.add_delta(8); |
|
127 } |
|
128 break; |
|
129 case ARM_EXIDX_CMD_WCGR_POP: |
|
130 // Pop wCGR registers under mask {wCGR3,2,1,0}, hence "i < 4" |
|
131 for (unsigned int i = 0; i < 4; i++) { |
|
132 if (edata->data & (1 << i)) { |
|
133 - vsp += " 4 +"; |
|
134 + vsp = vsp.add_delta(4); |
|
135 } |
|
136 } |
|
137 break; |
|
138 case ARM_EXIDX_CMD_REFUSED: |
|
139 case ARM_EXIDX_CMD_RESERVED: |
|
140 ret = -1; |
|
141 break; |
|
142 } |
|
143 return ret; |
|
144 } |
|
145 |
|
146 void ARMExToModule::AddStackFrame(uintptr_t addr, size_t size) { |
|
147 stack_frame_entry_ = new Module::StackFrameEntry; |
|
148 stack_frame_entry_->address = addr; |
|
149 stack_frame_entry_->size = size; |
|
150 - stack_frame_entry_->initial_rules[ToUniqueString(kCFA)] = Module::Expr("sp"); |
|
151 - vsp_ = "sp"; |
|
152 + Module::Expr sp_expr = Module::Expr(ustr__sp(), 0, false); // "sp" |
|
153 + stack_frame_entry_->initial_rules[ustr__ZDcfa()] = sp_expr; // ".cfa" |
|
154 + vsp_ = sp_expr; |
|
155 } |
|
156 |
|
157 int ARMExToModule::ImproveStackFrame(const struct extab_data* edata) { |
|
158 return TranslateCmd(edata, stack_frame_entry_, vsp_) ; |
|
159 } |
|
160 |
|
161 void ARMExToModule::DeleteStackFrame() { |
|
162 delete stack_frame_entry_; |
|
163 } |
|
164 |
|
165 void ARMExToModule::SubmitStackFrame() { |
|
166 // return address always winds up in pc |
|
167 - stack_frame_entry_->initial_rules[ToUniqueString(kRA)] |
|
168 + stack_frame_entry_->initial_rules[ustr__ZDra()] // ".ra" |
|
169 = stack_frame_entry_->initial_rules[ustr__pc()]; |
|
170 // the final value of vsp is the new value of sp |
|
171 stack_frame_entry_->initial_rules[ustr__sp()] = vsp_; |
|
172 module_->AddStackFrameEntry(stack_frame_entry_); |
|
173 } |
|
174 |
|
175 } // namespace arm_ex_to_module |
|
176 diff --git a/src/common/arm_ex_to_module.h b/src/common/arm_ex_to_module.h |
|
177 --- a/src/common/arm_ex_to_module.h |
|
178 +++ b/src/common/arm_ex_to_module.h |
|
179 @@ -89,19 +89,16 @@ struct extab_data { |
|
180 uint32_t data; |
|
181 }; |
|
182 |
|
183 enum extab_cmd_flags { |
|
184 ARM_EXIDX_VFP_SHIFT_16 = 1 << 16, |
|
185 ARM_EXIDX_VFP_FSTMD = 1 << 17, // distinguishes FSTMxxD from FSTMxxX |
|
186 }; |
|
187 |
|
188 -const string kRA = ".ra"; |
|
189 -const string kCFA = ".cfa"; |
|
190 - |
|
191 static const char* const regnames[] = { |
|
192 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
|
193 "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", |
|
194 "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", |
|
195 "fps", "cpsr" |
|
196 }; |
|
197 |
|
198 // Receives information from arm_ex_reader::ExceptionTableInfo |
|
199 @@ -113,17 +110,17 @@ class ARMExToModule { |
|
200 ~ARMExToModule() { } |
|
201 void AddStackFrame(uintptr_t addr, size_t size); |
|
202 int ImproveStackFrame(const struct extab_data* edata); |
|
203 void DeleteStackFrame(); |
|
204 void SubmitStackFrame(); |
|
205 private: |
|
206 Module* module_; |
|
207 Module::StackFrameEntry* stack_frame_entry_; |
|
208 - string vsp_; |
|
209 + Module::Expr vsp_; |
|
210 int TranslateCmd(const struct extab_data* edata, |
|
211 Module::StackFrameEntry* entry, |
|
212 - string& vsp); |
|
213 + Module::Expr& vsp); |
|
214 }; |
|
215 |
|
216 } // namespace arm_ex_to_module |
|
217 |
|
218 #endif // COMMON_ARM_EX_TO_MODULE__ |
|
219 diff --git a/src/common/module.h b/src/common/module.h |
|
220 --- a/src/common/module.h |
|
221 +++ b/src/common/module.h |
|
222 @@ -39,16 +39,20 @@ |
|
223 #define COMMON_LINUX_MODULE_H__ |
|
224 |
|
225 #include <iostream> |
|
226 #include <map> |
|
227 #include <set> |
|
228 #include <string> |
|
229 #include <vector> |
|
230 |
|
231 +#include <assert.h> |
|
232 +#include <stdlib.h> |
|
233 +#include <stdio.h> |
|
234 + |
|
235 #include "common/symbol_data.h" |
|
236 #include "common/using_std_string.h" |
|
237 #include "common/unique_string.h" |
|
238 #include "google_breakpad/common/breakpad_types.h" |
|
239 |
|
240 namespace google_breakpad { |
|
241 |
|
242 using std::set; |
|
243 @@ -161,30 +165,98 @@ class Module { |
|
244 // Construct an invalid expression |
|
245 Expr() { |
|
246 postfix_ = ""; |
|
247 ident_ = NULL; |
|
248 offset_ = 0; |
|
249 how_ = kExprInvalid; |
|
250 } |
|
251 bool isExprInvalid() const { return how_ == kExprInvalid; } |
|
252 - bool isExprPostfix() const { return how_ == kExprPostfix; } |
|
253 |
|
254 - // Return the postfix expression string. This is only |
|
255 - // meaningful on Exprs for which isExprPostfix returns true. |
|
256 - // In all other cases it returns an empty string. |
|
257 - string getExprPostfix() const { return postfix_; } |
|
258 + // Return the postfix expression string, either directly, |
|
259 + // if this is a postfix expression, or by synthesising it |
|
260 + // for a simple expression. |
|
261 + string getExprPostfix() const { |
|
262 + switch (how_) { |
|
263 + case kExprPostfix: |
|
264 + return postfix_; |
|
265 + case kExprSimple: |
|
266 + case kExprSimpleMem: { |
|
267 + char buf[40]; |
|
268 + sprintf(buf, " %ld %c%s", labs(offset_), offset_ < 0 ? '-' : '+', |
|
269 + how_ == kExprSimple ? "" : " ^"); |
|
270 + return string(FromUniqueString(ident_)) + string(buf); |
|
271 + } |
|
272 + case kExprInvalid: |
|
273 + default: |
|
274 + assert(0 && "getExprPostfix: invalid Module::Expr type"); |
|
275 + return "Expr::genExprPostfix: kExprInvalid"; |
|
276 + } |
|
277 + } |
|
278 |
|
279 bool operator==(const Expr& other) const { |
|
280 return how_ == other.how_ && |
|
281 ident_ == other.ident_ && |
|
282 offset_ == other.offset_ && |
|
283 postfix_ == other.postfix_; |
|
284 } |
|
285 |
|
286 + // Returns an Expr which evaluates to |this| + |delta| |
|
287 + Expr add_delta(long delta) { |
|
288 + if (delta == 0) { |
|
289 + return *this; |
|
290 + } |
|
291 + // If it's a simple form expression of the form "identifier + offset", |
|
292 + // simply add |delta| on to |offset|. In the other two possible |
|
293 + // cases: |
|
294 + // *(identifier + offset) |
|
295 + // completely arbitrary postfix expression string |
|
296 + // the only option is to "downgrade" it to a postfix expression and add |
|
297 + // "+/- delta" at the end of the string, since the result can't be |
|
298 + // represented in the simple form. |
|
299 + switch (how_) { |
|
300 + case kExprSimpleMem: |
|
301 + case kExprPostfix: { |
|
302 + char buf[40]; |
|
303 + sprintf(buf, " %ld %c", labs(delta), delta < 0 ? '-' : '+'); |
|
304 + return Expr(getExprPostfix() + string(buf)); |
|
305 + } |
|
306 + case kExprSimple: |
|
307 + return Expr(ident_, offset_ + delta, false); |
|
308 + case kExprInvalid: |
|
309 + default: |
|
310 + assert(0 && "add_delta: invalid Module::Expr type"); |
|
311 + // Invalid inputs produce an invalid result |
|
312 + return Expr(); |
|
313 + } |
|
314 + } |
|
315 + |
|
316 + // Returns an Expr which evaluates to *|this| |
|
317 + Expr deref() { |
|
318 + // In the simplest case, a kExprSimple can be changed into a |
|
319 + // kExprSimpleMem. In all other cases it has to be dumped as a |
|
320 + // postfix string, and " ^" added at the end. |
|
321 + switch (how_) { |
|
322 + case kExprSimple: { |
|
323 + Expr t = *this; |
|
324 + t.how_ = kExprSimpleMem; |
|
325 + return t; |
|
326 + } |
|
327 + case kExprSimpleMem: |
|
328 + case kExprPostfix: { |
|
329 + return Expr(getExprPostfix() + " ^"); |
|
330 + } |
|
331 + case kExprInvalid: |
|
332 + default: |
|
333 + assert(0 && "deref: invalid Module::Expr type"); |
|
334 + // Invalid inputs produce an invalid result |
|
335 + return Expr(); |
|
336 + } |
|
337 + } |
|
338 + |
|
339 // The identifier that gives the starting value for simple expressions. |
|
340 const UniqueString* ident_; |
|
341 // The offset to add for simple expressions. |
|
342 long offset_; |
|
343 // The Postfix expression string to evaluate for non-simple expressions. |
|
344 string postfix_; |
|
345 // The operation expressed by this expression. |
|
346 ExprHow how_; |