1 /*
2 * Copyright (C) 2024 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 #include "src/profiling/perf/frame_pointer_unwinder.h"
17
18 #include <cinttypes>
19
20 #include "perfetto/base/logging.h"
21
22 namespace perfetto {
23 namespace profiling {
24
Unwind()25 void FramePointerUnwinder::Unwind() {
26 if (!IsArchSupported()) {
27 PERFETTO_ELOG("Unsupported architecture: %d", arch_);
28 last_error_.code = unwindstack::ErrorCode::ERROR_UNSUPPORTED;
29 return;
30 }
31
32 if (maps_ == nullptr || maps_->Total() == 0) {
33 PERFETTO_ELOG("No maps provided");
34 last_error_.code = unwindstack::ErrorCode::ERROR_INVALID_MAP;
35 return;
36 }
37
38 PERFETTO_DCHECK(stack_size_ > 0u);
39
40 frames_.reserve(max_frames_);
41 ClearErrors();
42 TryUnwind();
43 }
44
TryUnwind()45 void FramePointerUnwinder::TryUnwind() {
46 uint64_t fp = 0;
47 switch (arch_) {
48 case unwindstack::ARCH_ARM64:
49 fp = reinterpret_cast<uint64_t*>(
50 regs_->RawData())[unwindstack::Arm64Reg::ARM64_REG_R29];
51 break;
52 case unwindstack::ARCH_X86_64:
53 fp = reinterpret_cast<uint64_t*>(
54 regs_->RawData())[unwindstack::X86_64Reg::X86_64_REG_RBP];
55 break;
56 case unwindstack::ARCH_RISCV64:
57 fp = reinterpret_cast<uint64_t*>(
58 regs_->RawData())[unwindstack::Riscv64Reg::RISCV64_REG_S0];
59 break;
60 case unwindstack::ARCH_UNKNOWN:
61 case unwindstack::ARCH_ARM:
62 case unwindstack::ARCH_X86:
63 // not supported
64 ;
65 }
66 uint64_t sp = regs_->sp();
67 uint64_t pc = regs_->pc();
68 for (size_t i = 0; i < max_frames_; i++) {
69 if (!IsFrameValid(fp, sp))
70 return;
71
72 // retrive the map info and elf info
73 std::shared_ptr<unwindstack::MapInfo> map_info = maps_->Find(pc);
74 if (map_info == nullptr) {
75 last_error_.code = unwindstack::ErrorCode::ERROR_INVALID_MAP;
76 return;
77 }
78
79 unwindstack::FrameData frame;
80 frame.num = i;
81 frame.rel_pc = pc;
82 frame.pc = pc;
83 frame.map_info = map_info;
84 unwindstack::Elf* elf = map_info->GetElf(process_memory_, arch_);
85 if (elf != nullptr) {
86 uint64_t relative_pc = elf->GetRelPc(pc, map_info.get());
87 uint64_t pc_adjustment = GetPcAdjustment(relative_pc, elf, arch_);
88 frame.rel_pc = relative_pc - pc_adjustment;
89 frame.pc = pc - pc_adjustment;
90 if (!resolve_names_ ||
91 !elf->GetFunctionName(frame.rel_pc, &frame.function_name,
92 &frame.function_offset)) {
93 frame.function_name = "";
94 frame.function_offset = 0;
95 }
96 }
97 frames_.push_back(frame);
98 // move to the next frame
99 fp = DecodeFrame(fp, &pc, &sp);
100 }
101 }
102
DecodeFrame(uint64_t fp,uint64_t * next_pc,uint64_t * next_sp)103 uint64_t FramePointerUnwinder::DecodeFrame(uint64_t fp,
104 uint64_t* next_pc,
105 uint64_t* next_sp) {
106 uint64_t next_fp;
107 if (!process_memory_->ReadFully(static_cast<uint64_t>(fp), &next_fp,
108 sizeof(next_fp)))
109 return 0;
110
111 uint64_t pc;
112 if (!process_memory_->ReadFully(static_cast<uint64_t>(fp + sizeof(uint64_t)),
113 &pc, sizeof(pc)))
114 return 0;
115
116 // Ensure there's not a stack overflow.
117 if (__builtin_add_overflow(fp, sizeof(uint64_t) * 2, next_sp))
118 return 0;
119
120 *next_pc = static_cast<uint64_t>(pc);
121 return next_fp;
122 }
123
IsFrameValid(uint64_t fp,uint64_t sp)124 bool FramePointerUnwinder::IsFrameValid(uint64_t fp, uint64_t sp) {
125 uint64_t align_mask = 0;
126 switch (arch_) {
127 case unwindstack::ARCH_ARM64:
128 align_mask = 0x1;
129 break;
130 case unwindstack::ARCH_X86_64:
131 align_mask = 0xf;
132 break;
133 case unwindstack::ARCH_RISCV64:
134 align_mask = 0x7;
135 break;
136 case unwindstack::ARCH_UNKNOWN:
137 case unwindstack::ARCH_ARM:
138 case unwindstack::ARCH_X86:
139 // not supported
140 ;
141 }
142
143 if (fp == 0 || fp <= sp)
144 return false;
145
146 // Ensure there's space on the stack to read two values: the caller's
147 // frame pointer and the return address.
148 uint64_t result;
149 if (__builtin_add_overflow(fp, sizeof(uint64_t) * 2, &result))
150 return false;
151
152 return result <= stack_end_ && (fp & align_mask) == 0;
153 }
154
155 } // namespace profiling
156 } // namespace perfetto
157