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, &regs, &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