• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sstream>
20 
21 #include <benchmark/benchmark.h>
22 
23 #include <unwindstack/DwarfLocation.h>
24 #include <unwindstack/DwarfSection.h>
25 
26 #include "Utils.h"
27 #include "utils/DwarfSectionImplFake.h"
28 #include "utils/MemoryFake.h"
29 #include "utils/RegsFake.h"
30 
31 namespace unwindstack {
32 namespace {
33 
34 // This collection of benchmarks exercises the DwarfSectionImpl::Eval function with a set of
35 // artificial unwind data. The number of registers and register evaluation method are varied
36 // for each individual benchmark.
37 
38 constexpr int kReturnAddressReg = 5;
39 
40 template <typename AddresssType>
41 class EvalBenchmark : public benchmark::Fixture {
42  public:
EvalBenchmark()43   EvalBenchmark() {
44     memory_.Clear();
45     section_ = std::make_unique<DwarfSectionImplFake<AddresssType>>(&memory_);
46   }
47 
TearDown(benchmark::State & state)48   void TearDown(benchmark::State& state) override { mem_tracker_.SetBenchmarkCounters(state); }
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     mem_tracker_.StartTrackingAllocations();
71     for (auto _ : state) {
72       std::stringstream err_stream;
73       if (!section_->Eval(&cie, &memory_, loc_regs, &regs, &finished)) {
74         err_stream << "Eval() failed at address " << section_->LastErrorAddress();
75         state.SkipWithError(err_stream.str().c_str());
76         return;
77       }
78       if (finished || regs.pc() != 0x60000000U || regs.sp() != 0x10000000U) {
79         err_stream
80             << "Eval() finished successfully but registers were not evaluated correctly."
81             << "\nExpected: finished == false, regs.pc() == 0x60000000, regs.sp() == 0x10000000."
82             << "\nActual: finished == " << std::boolalpha << finished << std::hex
83             << ", regs.pc() == 0x" << regs.pc() << ", regs.sp() == 0x" << regs.sp();
84         state.SkipWithError(err_stream.str().c_str());
85         return;
86       }
87     }
88     mem_tracker_.StopTrackingAllocations();
89   }
90 
91  protected:
92   MemoryFake memory_;
93   std::unique_ptr<DwarfSectionImplFake<AddresssType>> section_;
94   MemoryTracker mem_tracker_;
95 };
96 
97 // Benchmarks exercising Eval with the DWARF_LOCATION_REGISTER evaluation method.
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_register_few_regs,uint64_t)98 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_few_regs, uint64_t)(benchmark::State& state) {
99   DwarfLocations loc_regs;
100   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
101   loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0x50000000}};
102   RunBenchmark(state, loc_regs);
103 }
104 
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_register_many_regs,uint64_t)105 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_many_regs, uint64_t)(benchmark::State& state) {
106   DwarfLocations loc_regs;
107   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
108   for (uint64_t i = 0; i < 64; i++) {
109     loc_regs[i] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, i * 0x10000000}};
110   }
111   RunBenchmark(state, loc_regs);
112 }
113 
114 // Benchmarks exercising Eval with the DWARF_LOCATION_VAL_OFFSET evaluation method.
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_offset_few_regs,uint64_t)115 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_few_regs, uint64_t)
116 (benchmark::State& state) {
117   DwarfLocations loc_regs;
118   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
119   loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x50000000, 0}};
120   RunBenchmark(state, loc_regs);
121 }
122 
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_offset_many_regs,uint64_t)123 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_many_regs, uint64_t)
124 (benchmark::State& state) {
125   DwarfLocations loc_regs;
126   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
127   for (uint64_t i = 0; i < 64; i++) {
128     loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {i * 0x10000000, 0}};
129   }
130   RunBenchmark(state, loc_regs);
131 }
132 
133 // Benchmarks exercising Eval with the DWARF_LOCATION_OFFSET evaluation method.
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_offset_few_regs,uint64_t)134 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_few_regs, uint64_t)
135 (benchmark::State& state) {
136   memory_.SetData64(0x20000000, 0x60000000);
137   DwarfLocations loc_regs;
138   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
139   loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
140   RunBenchmark(state, loc_regs);
141 }
142 
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_offset_many_regs,uint64_t)143 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_many_regs, uint64_t)
144 (benchmark::State& state) {
145   memory_.SetData64(0x20000000, 0x60000000);
146   memory_.SetData64(0x30000000, 0x10000000);
147   DwarfLocations loc_regs;
148   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
149   for (uint64_t i = 1; i < 64; i++) {
150     loc_regs[i] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
151   }
152   // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
153   // across multiple calls to Eval.
154   loc_regs[0] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x20000000, 0}};
155   RunBenchmark(state, loc_regs);
156 }
157 
158 // Benchmarks exercising Eval with the DWARF_LOCATION_EXPRESSION evaluation method.
159 // 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)160 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_few_regs, uint64_t)
161 (benchmark::State& state) {
162   memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
163   uint64_t pc_value = 0x60000000;
164   memory_.SetMemory(0x80000000, &pc_value, sizeof(pc_value));
165   DwarfLocations loc_regs;
166   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
167   loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
168   RunBenchmark(state, loc_regs);
169 }
170 
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_expression_many_regs,uint64_t)171 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_many_regs, uint64_t)
172 (benchmark::State& state) {
173   memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
174   uint64_t pc_value = 0x60000000;
175   memory_.SetMemory(0x80000000, &pc_value, sizeof(pc_value));
176 
177   memory_.SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x90});
178   uint64_t sp_value = 0x10000000;
179   memory_.SetMemory(0x90000000, &sp_value, sizeof(sp_value));
180 
181   DwarfLocations loc_regs;
182   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
183   for (uint64_t i = 1; i < 64; i++) {
184     loc_regs[i] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
185   }
186   // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
187   // across multiple calls to Eval.
188   loc_regs[0] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x6004}};
189   RunBenchmark(state, loc_regs);
190 }
191 
192 // Benchmarks exercising Eval with the DWARF_LOCATION_VAL_EXPRESSION evaluation method.
193 // 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)194 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_few_regs, uint64_t)
195 (benchmark::State& state) {
196   memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
197   DwarfLocations loc_regs;
198   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
199   loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
200   RunBenchmark(state, loc_regs);
201 }
202 
BENCHMARK_TEMPLATE_F(EvalBenchmark,BM_eval_val_expression_many_regs,uint64_t)203 BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_many_regs, uint64_t)
204 (benchmark::State& state) {
205   memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
206   memory_.SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x10});
207   DwarfLocations loc_regs;
208   loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
209   for (uint64_t i = 1; i < 64; i++) {
210     loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
211   }
212   // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
213   // across multiple calls to Eval.
214   loc_regs[0] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x6004}};
215   RunBenchmark(state, loc_regs);
216 }
217 
218 }  // namespace
219 }  // namespace unwindstack
220