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