xref: /aosp_15_r20/external/perfetto/src/profiling/perf/frame_pointer_unwinder.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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