dmp文件分析(四)- 调用栈分析
| 阅读 | 共 1863 字,阅读约
Overview
dmp文件分析(四)- 调用栈分析
前面的章节我们介绍过,分析dmp的一个调用栈的过程,以及获取上一个栈帧的公式。今天介绍这个公式在breakpad中的源码调用。调用栈分析有三种途径,里面很多细节还不是完全理解,只是大概罗列一下代码的框架
获取调用者的栈帧
前面介绍过Stackwalker方法是基类,每种不同的cpu架构都有自己的实现类,都需要重写几个方法,其中很重要的一个是GetCallerFrame方法,用于获取调用栈的上一个栈帧
caller: 调用者(父函数); callee:被调用者(子函数)
这里以常用的amd64架构cpu介绍源码实现。实现类为 StackwalkerAMD64
1StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack* stack,
2 bool stack_scan_allowed) {
3 ...
4 // 先获取调用栈所有的栈帧
5 const vector<StackFrame*> &frames = *stack->frames();
6 // 取出最后一个栈帧
7 StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
8 scoped_ptr<StackFrameAMD64> new_frame;
9
10 // 如果symbol中有CFI信息,查找栈帧相关的CFI信息
11 scoped_ptr<CFIFrameInfo> cfi_frame_info(
12 frame_symbolizer_->FindCFIFrameInfo(last_frame));
13 if (cfi_frame_info.get())
14 // 通过CFI信息获取上次调用函数的栈帧
15 new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
16
17 // If CFI was not available or failed, try using frame pointer recovery.
18 // 如果CFI不可用或者获取失败,尝试用栈帧结构中的指针恢复调用关系
19 if (!new_frame.get()) {
20 new_frame.reset(GetCallerByFramePointerRecovery(frames));
21 }
22
23 // If all else fails, fall back to stack scanning.
24 // 如果上面的方法都获取失败,退回堆栈扫描
25 if (stack_scan_allowed && !new_frame.get()) {
26 new_frame.reset(GetCallerByStackScan(frames));
27 }
28
29 // If nothing worked, tell the caller.
30 if (!new_frame.get())
31 return NULL;
32
33 // 判断是否应该终止栈帧查找(找到了栈尾,或者栈帧被损坏了)
34 // Should we terminate the stack walk? (end-of-stack or broken invariant)
35 if (TerminateWalk(new_frame->context.rip, new_frame->context.rsp,
36 last_frame->context.rsp, frames.size() == 1)) {
37 return NULL;
38 }
39
40 // new_frame->context.rip是返回地址,即在调用CALL指令之后,被调用者的地址
41 // new_frame->instruction 的值比它小一点,指向CALL指令
42 new_frame->instruction = new_frame->context.rip - 1;
43
44 return new_frame.release();
45}
根据CFI分析上层栈帧
- CFI 相关知识目前还不太清楚,后续会专门研究一下并整理文档
查找symbol的CFI
1CFIFrameInfo *SourceLineResolverBase::FindCFIFrameInfo(
2 const StackFrame *frame) {
3 if (frame->module) {
4 // 根据模块的文件名为key,查询模块信息
5 ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
6 if (it != modules_->end()) {
7 return it->second->FindCFIFrameInfo(frame);
8 }
9 }
10 return NULL;
11}
12
13// 下面部分为从symbol读取的CFI信息,并且保存在一个RangeMap中
14CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo(
15 const StackFrame *frame) const {
16 MemAddr address = frame->instruction - frame->module->base_address();
17 MemAddr initial_base, initial_size;
18 string initial_rules;
19
20 // Find the initial rule whose range covers this address. That
21 // provides an initial set of register recovery rules. Then, walk
22 // forward from the initial rule's starting address to frame's
23 // instruction address, applying delta rules.
24 if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules, &initial_base,
25 NULL /* delta */, &initial_size)) {
26 return NULL;
27 }
28
29 // Create a frame info structure, and populate it with the rules from
30 // the STACK CFI INIT record.
31 scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
32 if (!ParseCFIRuleSet(initial_rules, rules.get()))
33 return NULL;
34
35 // Find the first delta rule that falls within the initial rule's range.
36 map<MemAddr, string>::const_iterator delta =
37 cfi_delta_rules_.lower_bound(initial_base);
38
39 // Apply delta rules up to and including the frame's address.
40 while (delta != cfi_delta_rules_.end() && delta->first <= address) {
41 ParseCFIRuleSet(delta->second, rules.get());
42 delta++;
43 }
44
45 return rules.release();
46}
根据CFI查找堆栈
1StackFrameAMD64* StackwalkerAMD64::GetCallerByCFIFrameInfo(
2 const vector<StackFrame*> &frames,
3 CFIFrameInfo* cfi_frame_info) {
4 StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
5
6 scoped_ptr<StackFrameAMD64> frame(new StackFrameAMD64());
7 if (!cfi_walker_
8 .FindCallerRegisters(*memory_, *cfi_frame_info,
9 last_frame->context, last_frame->context_validity,
10 &frame->context, &frame->context_validity))
11 return NULL;
12
13 // Make sure we recovered all the essentials.
14 static const int essentials = (StackFrameAMD64::CONTEXT_VALID_RIP
15 | StackFrameAMD64::CONTEXT_VALID_RSP);
16 if ((frame->context_validity & essentials) != essentials)
17 return NULL;
18
19 frame->trust = StackFrame::FRAME_TRUST_CFI;
20 return frame.release();
21}
根据栈帧结构分析上层栈帧
- 这里利用了上一节分析到的栈帧结构中的几个公式
1StackFrameAMD64* StackwalkerAMD64::GetCallerByFramePointerRecovery(
2 const vector<StackFrame*>& frames) {
3
4 // 获取上一个栈帧的 rbp 寄存器
5 StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
6 uint64_t last_rbp = last_frame->context.rbp;
7
8 // 下面利用上一篇文章说到的栈帧结构公式,计算下一个栈帧的寄存器地址
9
10 // Assume the presence of a frame pointer. This is not mandated by the
11 // AMD64 ABI, c.f. section 3.2.2 footnote 7, though it is typical for
12 // compilers to still preserve the frame pointer and not treat %rbp as a
13 // general purpose register.
14 //
15 // With this assumption, the CALL instruction pushes the return address
16 // onto the stack and sets %rip to the procedure to enter. The procedure
17 // then establishes the stack frame with a prologue that PUSHes the current
18 // %rbp onto the stack, MOVes the current %rsp to %rbp, and then allocates
19 // space for any local variables. Using this procedure linking information,
20 // it is possible to locate frame information for the callee:
21 //
22 // %caller_rsp = *(%callee_rbp + 16)
23 // %caller_rip = *(%callee_rbp + 8)
24 // %caller_rbp = *(%callee_rbp)
25
26 // If rbp is not 8-byte aligned it can't be a frame pointer.
27 if (last_rbp % 8 != 0) {
28 return NULL;
29 }
30
31 uint64_t caller_rip, caller_rbp;
32 if (memory_->GetMemoryAtAddress(last_rbp + 8, &caller_rip) &&
33 memory_->GetMemoryAtAddress(last_rbp, &caller_rbp)) {
34 uint64_t caller_rsp = last_rbp + 16;
35
36 StackFrameAMD64* frame = new StackFrameAMD64();
37 frame->trust = StackFrame::FRAME_TRUST_FP;
38 frame->context = last_frame->context;
39 frame->context.rip = caller_rip;
40 frame->context.rsp = caller_rsp;
41 frame->context.rbp = caller_rbp;
42 frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
43 StackFrameAMD64::CONTEXT_VALID_RSP |
44 StackFrameAMD64::CONTEXT_VALID_RBP;
45 return frame;
46 }
47
48 return NULL;
49}
扫描栈获取下一个栈帧的信息
- 这个地方也还没有看明白
1StackFrameAMD64* StackwalkerAMD64::GetCallerByStackScan(
2 const vector<StackFrame*> &frames) {
3 StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
4 uint64_t last_rsp = last_frame->context.rsp;
5 uint64_t caller_rip_address, caller_rip;
6
7 // 查找寄存器中的返回地址
8 if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip,
9 frames.size() == 1 /* is_context_frame */)) {
10 // No plausible return address was found.
11 return NULL;
12 }
13
14 // Create a new stack frame (ownership will be transferred to the caller)
15 // and fill it in.
16 StackFrameAMD64* frame = new StackFrameAMD64();
17
18 frame->trust = StackFrame::FRAME_TRUST_SCAN;
19 frame->context = last_frame->context;
20 frame->context.rip = caller_rip;
21 // The caller's %rsp is directly underneath the return address pushed by
22 // the call.
23 // 栈帧的 esp地址 = 调用者的 eip + 8
24 frame->context.rsp = caller_rip_address + 8;
25 frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
26 StackFrameAMD64::CONTEXT_VALID_RSP;
27
28 // Other unwinders give up if they don't have an %rbp value, so see if we
29 // can pass some plausible value on.
30 if (last_frame->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) {
31 // Functions typically push their caller's %rbp immediately upon entry,
32 // and then set %rbp to point to that. So if the callee's %rbp is
33 // pointing to the first word below the alleged return address, presume
34 // that the caller's %rbp is saved there.
35 if (caller_rip_address - 8 == last_frame->context.rbp) {
36 uint64_t caller_rbp = 0;
37 if (memory_->GetMemoryAtAddress(last_frame->context.rbp, &caller_rbp) &&
38 caller_rbp > caller_rip_address) {
39 frame->context.rbp = caller_rbp;
40 frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP;
41 }
42 } else if (last_frame->context.rbp >= caller_rip_address + 8) {
43 // If the callee's %rbp is plausible as a value for the caller's
44 // %rbp, presume that the callee left it unchanged.
45 frame->context.rbp = last_frame->context.rbp;
46 frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP;
47 }
48 }
49 return frame;
50}
1template<typename InstructionType>
2 bool ScanForReturnAddress(InstructionType location_start,
3 InstructionType* location_found,
4 InstructionType* ip_found,
5 bool is_context_frame) {
6 // When searching for the caller of the context frame,
7 // allow the scanner to look farther down the stack.
8 const int search_words = is_context_frame ?
9 kRASearchWords * 4 :
10 kRASearchWords;
11
12 return ScanForReturnAddress(location_start, location_found, ip_found,
13 search_words);
14 }
15
16template<typename InstructionType>
17 bool ScanForReturnAddress(InstructionType location_start,
18 InstructionType* location_found,
19 InstructionType* ip_found,
20 int searchwords) {
21 for (InstructionType location = location_start;
22 location <= location_start + searchwords * sizeof(InstructionType);
23 location += sizeof(InstructionType)) {
24 InstructionType ip;
25 if (!memory_->GetMemoryAtAddress(location, &ip))
26 break;
27
28 if (modules_ && modules_->GetModuleForAddress(ip) &&
29 InstructionAddressSeemsValid(ip)) {
30 *ip_found = ip;
31 *location_found = location;
32 return true;
33 }
34 }
35 // nothing found
36 return false;
37 }