1 /*
2 * Copyright (C) 2021 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
17 #include <cstdint>
18 #include <ios>
19 #include <memory>
20 #include <sstream>
21
22 #include <benchmark/benchmark.h>
23
24 #include <unwindstack/DwarfLocation.h>
25 #include <unwindstack/DwarfSection.h>
26
27 #include "Utils.h"
28 #include "utils/DwarfSectionImplFake.h"
29 #include "utils/MemoryFake.h"
30 #include "utils/RegsFake.h"
31
32 namespace unwindstack {
33 namespace {
34
35 // This collection of benchmarks exercises the DwarfSectionImpl::Eval function with a set of
36 // artificial unwind data. The number of registers and register evaluation method are varied
37 // for each individual benchmark.
38
39 constexpr int kReturnAddressReg = 5;
40
41 template <typename AddresssType>
42 class EvalBenchmark : public benchmark::Fixture {
43 public:
EvalBenchmark()44 EvalBenchmark() {
45 fake_memory_ = new MemoryFake;
46 std::shared_ptr<Memory> memory(fake_memory_);
47 section_ = std::make_unique<DwarfSectionImplFake<AddresssType>>(memory);
48 }
49
50 // Benchmarks DwarfSectionImpl::Eval given the DwarfLocation object, loc_regs, initialized in each
51 // individual benchmark macro/function.
52 //
53 // This method initializes the fake register object and the DwarfCie object the same regardless
54 // of the benchmark. So the initialization of loc_regs is carefully crafted in each benchmark
55 // macro so that the evaluated PC and SP match the expected values after each call to Eval in the
56 // benchmarking loop.
57 //
58 // In addition to the Eval call, register value assertion is included in the benchmarking loop
59 // to ensure that we always capture the actual register evaluation
60 // (DwarfSectionImpl::EvalRegister). For example, if Eval is modified to lazily evaluate register
61 // values, we will still capture the register evaluation for the PC and SP (common case) in the
62 // register value assertion.
RunBenchmark(benchmark::State & state,DwarfLocations & loc_regs)63 void RunBenchmark(benchmark::State& state, DwarfLocations& loc_regs) {
64 DwarfCie cie{.return_address_register = kReturnAddressReg};
65 bool finished;
66 RegsImplFake<AddresssType> regs(64);
67 regs.set_pc(0x1000);
68 regs.set_sp(0x3500);
69 regs[0] = 0x10000000;
70 MemoryTracker mem_tracker;
71 for (const auto& _ : state) {
72 state.PauseTiming();
73 mem_tracker.StartTrackingAllocations();
74 state.ResumeTiming();
75
76 std::stringstream err_stream;
77 if (!section_->Eval(&cie, fake_memory_, loc_regs, ®s, &finished)) {
78 err_stream << "Eval() failed at address " << section_->LastErrorAddress();
79 state.SkipWithError(err_stream.str().c_str());
80 return;
81 }
82 if (finished || regs.pc() != 0x60000000U || regs.sp() != 0x10000000U) {
83 err_stream
84 << "Eval() finished successfully but registers were not evaluated correctly."
85 << "\nExpected: finished == false, regs.pc() == 0x60000000, regs.sp() == 0x10000000."
86 << "\nActual: finished == " << std::boolalpha << finished << std::hex
87 << ", regs.pc() == 0x" << regs.pc() << ", regs.sp() == 0x" << regs.sp();
88 state.SkipWithError(err_stream.str().c_str());
89 return;
90 }
91 state.PauseTiming();
92 mem_tracker.StopTrackingAllocations();
93 state.ResumeTiming();
94 }
95 mem_tracker.SetBenchmarkCounters(state);
96 }
97
98 protected:
99 MemoryFake* fake_memory_;
100 std::unique_ptr<DwarfSectionImplFake<AddresssType>> section_;
101 };
102
103 // Benchmarks exercising Eval with the DWARF_LOCATION_REGISTER evaluation method.
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_register_few_regs,uint64_t)104 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_few_regs, uint64_t)(benchmark::State& state) {
105 DwarfLocations loc_regs;
106 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
107 loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0x50000000}};
108 RunBenchmark(state, loc_regs);
109 }
110
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_register_many_regs,uint64_t)111 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_many_regs, uint64_t)(benchmark::State& state) {
112 DwarfLocations loc_regs;
113 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
114 for (uint64_t i = 0; i < 64; i++) {
115 loc_regs[i] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, i * 0x10000000}};
116 }
117 RunBenchmark(state, loc_regs);
118 }
119
120 // Benchmarks exercising Eval with the DWARF_LOCATION_VAL_OFFSET evaluation method.
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_offset_few_regs,uint64_t)121 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_few_regs, uint64_t)
122 (benchmark::State& state) {
123 DwarfLocations loc_regs;
124 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
125 loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x50000000, 0}};
126 RunBenchmark(state, loc_regs);
127 }
128
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_offset_many_regs,uint64_t)129 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_many_regs, uint64_t)
130 (benchmark::State& state) {
131 DwarfLocations loc_regs;
132 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
133 for (uint64_t i = 0; i < 64; i++) {
134 loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {i * 0x10000000, 0}};
135 }
136 RunBenchmark(state, loc_regs);
137 }
138
139 // Benchmarks exercising Eval with the DWARF_LOCATION_OFFSET evaluation method.
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_offset_few_regs,uint64_t)140 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_few_regs, uint64_t)
141 (benchmark::State& state) {
142 fake_memory_->SetData64(0x20000000, 0x60000000);
143 DwarfLocations loc_regs;
144 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
145 loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
146 RunBenchmark(state, loc_regs);
147 }
148
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_offset_many_regs,uint64_t)149 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_many_regs, uint64_t)
150 (benchmark::State& state) {
151 fake_memory_->SetData64(0x20000000, 0x60000000);
152 fake_memory_->SetData64(0x30000000, 0x10000000);
153 DwarfLocations loc_regs;
154 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
155 for (uint64_t i = 1; i < 64; i++) {
156 loc_regs[i] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
157 }
158 // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
159 // across multiple calls to Eval.
160 loc_regs[0] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x20000000, 0}};
161 RunBenchmark(state, loc_regs);
162 }
163
164 // Benchmarks exercising Eval with the DWARF_LOCATION_EXPRESSION evaluation method.
165 // The dwarf op-code used for the expression benchmarks are OP_const4u (see DwarfOp::Eval).
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_expression_few_regs,uint64_t)166 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_few_regs, uint64_t)
167 (benchmark::State& state) {
168 fake_memory_->SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
169 uint64_t pc_value = 0x60000000;
170 fake_memory_->SetMemory(0x80000000, &pc_value, sizeof(pc_value));
171 DwarfLocations loc_regs;
172 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
173 loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
174 RunBenchmark(state, loc_regs);
175 }
176
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_expression_many_regs,uint64_t)177 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_many_regs, uint64_t)
178 (benchmark::State& state) {
179 fake_memory_->SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
180 uint64_t pc_value = 0x60000000;
181 fake_memory_->SetMemory(0x80000000, &pc_value, sizeof(pc_value));
182
183 fake_memory_->SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x90});
184 uint64_t sp_value = 0x10000000;
185 fake_memory_->SetMemory(0x90000000, &sp_value, sizeof(sp_value));
186
187 DwarfLocations loc_regs;
188 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
189 for (uint64_t i = 1; i < 64; i++) {
190 loc_regs[i] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
191 }
192 // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
193 // across multiple calls to Eval.
194 loc_regs[0] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x6004}};
195 RunBenchmark(state, loc_regs);
196 }
197
198 // Benchmarks exercising Eval with the DWARF_LOCATION_VAL_EXPRESSION evaluation method.
199 // The dwarf op-code used for the value expression benchmarks are OP_const4u (see DwarfOp::Eval).
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_expression_few_regs,uint64_t)200 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_few_regs, uint64_t)
201 (benchmark::State& state) {
202 fake_memory_->SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
203 DwarfLocations loc_regs;
204 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
205 loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
206 RunBenchmark(state, loc_regs);
207 }
208
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_expression_many_regs,uint64_t)209 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_many_regs, uint64_t)
210 (benchmark::State& state) {
211 fake_memory_->SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
212 fake_memory_->SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x10});
213 DwarfLocations loc_regs;
214 loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
215 for (uint64_t i = 1; i < 64; i++) {
216 loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
217 }
218 // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
219 // across multiple calls to Eval.
220 loc_regs[0] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x6004}};
221 RunBenchmark(state, loc_regs);
222 }
223
224 } // namespace
225 } // namespace unwindstack
226