blob: a83f85b9c97f2b6ca1a32a8f6025c6650a5a7eb4 [file] [log] [blame]
Christopher Ferrisb9de87f2017-09-20 13:37:24 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070017#define _GNU_SOURCE 1
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070018#include <elf.h>
19#include <inttypes.h>
20#include <stdint.h>
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070021#include <string.h>
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070022#include <sys/types.h>
23#include <unistd.h>
24
Christopher Ferris1cb84ce2017-10-24 15:36:00 -070025#include <algorithm>
26
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070027#include <android-base/stringprintf.h>
28
29#include <unwindstack/Elf.h>
30#include <unwindstack/MapInfo.h>
31#include <unwindstack/Unwinder.h>
32
33namespace unwindstack {
34
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080035void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070036 size_t frame_num = frames_.size();
37 frames_.resize(frame_num + 1);
38 FrameData* frame = &frames_.at(frame_num);
39 frame->num = frame_num;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070040 frame->sp = regs_->sp();
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080041 frame->rel_pc = adjusted_rel_pc;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070042
43 if (map_info == nullptr) {
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080044 frame->pc = regs_->pc();
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070045 return;
46 }
47
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080048 frame->pc = map_info->start + adjusted_rel_pc;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070049 frame->map_name = map_info->name;
Christopher Ferrisc5a3baa2017-10-17 18:42:03 -070050 frame->map_offset = map_info->offset;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070051 frame->map_start = map_info->start;
52 frame->map_end = map_info->end;
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070053 frame->map_flags = map_info->flags;
54 frame->map_load_bias = elf->GetLoadBias();
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070055
56 if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
57 frame->function_name = "";
58 frame->function_offset = 0;
59 }
60}
61
Christopher Ferris1cb84ce2017-10-24 15:36:00 -070062static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
63 std::string& map_name) {
Christopher Ferrise69f4702017-10-19 16:08:58 -070064 if (map_suffixes_to_ignore == nullptr) {
65 return false;
66 }
67 auto pos = map_name.find_last_of('.');
68 if (pos == std::string::npos) {
69 return false;
70 }
Christopher Ferris1cb84ce2017-10-24 15:36:00 -070071
72 return std::find(map_suffixes_to_ignore->begin(), map_suffixes_to_ignore->end(),
73 map_name.substr(pos + 1)) != map_suffixes_to_ignore->end();
Christopher Ferrise69f4702017-10-19 16:08:58 -070074}
75
Christopher Ferris1cb84ce2017-10-24 15:36:00 -070076void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
77 const std::vector<std::string>* map_suffixes_to_ignore) {
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070078 frames_.clear();
79
80 bool return_address_attempt = false;
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070081 bool adjust_pc = false;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070082 for (; frames_.size() < max_frames_;) {
Christopher Ferrisfda7edd2017-10-31 16:10:42 -070083 uint64_t cur_pc = regs_->pc();
84 uint64_t cur_sp = regs_->sp();
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070085
Christopher Ferrisfda7edd2017-10-31 16:10:42 -070086 MapInfo* map_info = maps_->Find(regs_->pc());
Christopher Ferrisb9de87f2017-09-20 13:37:24 -070087 uint64_t rel_pc;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080088 uint64_t adjusted_rel_pc;
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070089 Elf* elf;
90 if (map_info == nullptr) {
91 rel_pc = regs_->pc();
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080092 adjusted_rel_pc = rel_pc;
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070093 } else {
Christopher Ferrise69f4702017-10-19 16:08:58 -070094 if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
95 break;
96 }
Christopher Ferrisf6f691b2017-09-25 19:23:07 -070097 elf = map_info->GetElf(process_memory_, true);
98 rel_pc = elf->GetRelPc(regs_->pc(), map_info);
Christopher Ferrisc3d79f72017-11-28 19:14:54 -080099 if (adjust_pc) {
100 adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
101 } else {
102 adjusted_rel_pc = rel_pc;
103 }
Christopher Ferrisf6f691b2017-09-25 19:23:07 -0700104 }
105
106 if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
Christopher Ferris1cb84ce2017-10-24 15:36:00 -0700107 std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
108 basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800109 FillInFrame(map_info, elf, adjusted_rel_pc);
110
Christopher Ferrisf6f691b2017-09-25 19:23:07 -0700111 // Once a frame is added, stop skipping frames.
112 initial_map_names_to_skip = nullptr;
113 }
114 adjust_pc = true;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700115
116 bool stepped;
Christopher Ferrisf6f691b2017-09-25 19:23:07 -0700117 bool in_device_map = false;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700118 if (map_info == nullptr) {
119 stepped = false;
120 } else {
Christopher Ferrisf6f691b2017-09-25 19:23:07 -0700121 if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
122 // Do not stop here, fall through in case we are
123 // in the speculative unwind path and need to remove
124 // some of the speculative frames.
125 stepped = false;
126 in_device_map = true;
127 } else {
128 MapInfo* sp_info = maps_->Find(regs_->sp());
129 if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
130 // Do not stop here, fall through in case we are
131 // in the speculative unwind path and need to remove
132 // some of the speculative frames.
133 stepped = false;
134 in_device_map = true;
135 } else {
136 bool finished;
Christopher Ferrisc3d79f72017-11-28 19:14:54 -0800137 stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
138 process_memory_.get(), &finished);
Christopher Ferrisf6f691b2017-09-25 19:23:07 -0700139 if (stepped && finished) {
140 break;
141 }
142 }
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700143 }
144 }
Christopher Ferrisfda7edd2017-10-31 16:10:42 -0700145
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700146 if (!stepped) {
147 if (return_address_attempt) {
148 // Remove the speculative frame.
149 frames_.pop_back();
150 break;
Christopher Ferrisf6f691b2017-09-25 19:23:07 -0700151 } else if (in_device_map) {
152 // Do not attempt any other unwinding, pc or sp is in a device
153 // map.
154 break;
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700155 } else {
156 // Steping didn't work, try this secondary method.
157 if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
158 break;
159 }
160 return_address_attempt = true;
161 }
162 } else {
163 return_address_attempt = false;
164 }
Christopher Ferrisfda7edd2017-10-31 16:10:42 -0700165
166 // If the pc and sp didn't change, then consider everything stopped.
167 if (cur_pc == regs_->pc() && cur_sp == regs_->sp()) {
168 break;
169 }
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700170 }
171}
172
173std::string Unwinder::FormatFrame(size_t frame_num) {
174 if (frame_num >= frames_.size()) {
175 return "";
176 }
Christopher Ferris02fdb562017-12-08 15:04:49 -0800177 return FormatFrame(frames_[frame_num], regs_->Format32Bit());
Christopher Ferrisb9de87f2017-09-20 13:37:24 -0700178}
179
180std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
181 std::string data;
182
183 if (bits32) {
184 data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
185 } else {
186 data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
187 }
188
189 if (frame.map_offset != 0) {
190 data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
191 }
192
193 if (frame.map_start == frame.map_end) {
194 // No valid map associated with this frame.
195 data += " <unknown>";
196 } else if (!frame.map_name.empty()) {
197 data += " " + frame.map_name;
198 } else {
199 data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
200 }
201 if (!frame.function_name.empty()) {
202 data += " (" + frame.function_name;
203 if (frame.function_offset != 0) {
204 data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
205 }
206 data += ')';
207 }
208 return data;
209}
210
211} // namespace unwindstack