#![feature(test)]
extern crate gimli;
extern crate test;
use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugLineOffset,
DebugLoc, DebugPubNames, DebugPubTypes, DebugRanges, EntriesTreeNode, Expression,
Format, LittleEndian, Operation, Reader};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
pub fn read_section(section: &str) -> Vec<u8> {
let mut path = PathBuf::from(
env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()),
);
path.push("./fixtures/self/");
path.push(section);
assert!(path.is_file());
let mut file = File::open(path).unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
buf
}
#[bench]
fn bench_parsing_debug_abbrev(b: &mut test::Bencher) {
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let unit = debug_info
.units()
.next()
.expect("Should have at least one compilation unit")
.expect("And it should parse OK");
let debug_abbrev = read_section("debug_abbrev");
b.iter(|| {
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
test::black_box(
unit.abbreviations(&debug_abbrev)
.expect("Should parse abbreviations"),
);
});
}
#[bench]
fn bench_parsing_debug_info(b: &mut test::Bencher) {
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_info = read_section("debug_info");
b.iter(|| {
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let mut iter = debug_info.units();
while let Some(unit) = iter.next().expect("Should parse compilation unit") {
let abbrevs = unit.abbreviations(&debug_abbrev)
.expect("Should parse abbreviations");
let mut cursor = unit.entries(&abbrevs);
while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") {
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
test::black_box(&attr);
}
}
}
});
}
#[bench]
fn bench_parsing_debug_info_tree(b: &mut test::Bencher) {
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_info = read_section("debug_info");
b.iter(|| {
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let mut iter = debug_info.units();
while let Some(unit) = iter.next().expect("Should parse compilation unit") {
let abbrevs = unit.abbreviations(&debug_abbrev)
.expect("Should parse abbreviations");
let mut tree = unit.entries_tree(&abbrevs, None)
.expect("Should have entries tree");
let root = tree.root().expect("Should parse root entry");
parse_debug_info_tree(root);
}
});
}
fn parse_debug_info_tree<R: Reader>(node: EntriesTreeNode<R>) {
{
let mut attrs = node.entry().attrs();
while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
test::black_box(&attr);
}
}
let mut children = node.children();
while let Some(child) = children.next().expect("Should parse child entry") {
parse_debug_info_tree(child);
}
}
#[bench]
fn bench_parsing_debug_aranges(b: &mut test::Bencher) {
let debug_aranges = read_section("debug_aranges");
let debug_aranges = DebugAranges::new(&debug_aranges, LittleEndian);
b.iter(|| {
let mut aranges = debug_aranges.items();
while let Some(arange) = aranges.next().expect("Should parse arange OK") {
test::black_box(arange);
}
});
}
#[bench]
fn bench_parsing_debug_pubnames(b: &mut test::Bencher) {
let debug_pubnames = read_section("debug_pubnames");
let debug_pubnames = DebugPubNames::new(&debug_pubnames, LittleEndian);
b.iter(|| {
let mut pubnames = debug_pubnames.items();
while let Some(pubname) = pubnames.next().expect("Should parse pubname OK") {
test::black_box(pubname);
}
});
}
#[bench]
fn bench_parsing_debug_pubtypes(b: &mut test::Bencher) {
let debug_pubtypes = read_section("debug_pubtypes");
let debug_pubtypes = DebugPubTypes::new(&debug_pubtypes, LittleEndian);
b.iter(|| {
let mut pubtypes = debug_pubtypes.items();
while let Some(pubtype) = pubtypes.next().expect("Should parse pubtype OK") {
test::black_box(pubtype);
}
});
}
const OFFSET: DebugLineOffset = DebugLineOffset(0);
const ADDRESS_SIZE: u8 = 8;
#[bench]
fn bench_parsing_line_number_program_opcodes(b: &mut test::Bencher) {
let debug_line = read_section("debug_line");
let debug_line = DebugLine::new(&debug_line, LittleEndian);
b.iter(|| {
let program = debug_line
.program(OFFSET, ADDRESS_SIZE, None, None)
.expect("Should parse line number program header");
let header = program.header();
let mut opcodes = header.opcodes();
while let Some(opcode) = opcodes.next_opcode(header).expect("Should parse opcode") {
test::black_box(opcode);
}
});
}
#[bench]
fn bench_executing_line_number_programs(b: &mut test::Bencher) {
let debug_line = read_section("debug_line");
let debug_line = DebugLine::new(&debug_line, LittleEndian);
b.iter(|| {
let program = debug_line
.program(OFFSET, ADDRESS_SIZE, None, None)
.expect("Should parse line number program header");
let mut rows = program.rows();
while let Some(row) = rows.next_row().expect(
"Should parse and execute all rows in the line number program",
) {
test::black_box(row);
}
});
}
#[bench]
fn bench_parsing_debug_loc(b: &mut test::Bencher) {
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_loc = read_section("debug_loc");
let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
let mut offsets = Vec::new();
let mut iter = debug_info.units();
while let Some(unit) = iter.next().expect("Should parse compilation unit") {
let abbrevs = unit.abbreviations(&debug_abbrev)
.expect("Should parse abbreviations");
let mut cursor = unit.entries(&abbrevs);
cursor.next_dfs().expect("Should parse next dfs");
let mut low_pc = 0;
{
let unit_entry = cursor.current().expect("Should have a root entry");
let low_pc_attr = unit_entry
.attr_value(gimli::DW_AT_low_pc)
.expect("Should parse low_pc");
if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
low_pc = address;
}
}
while cursor.next_dfs().expect("Should parse next dfs").is_some() {
let entry = cursor.current().expect("Should have a current entry");
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
if let gimli::AttributeValue::DebugLocRef(offset) = attr.value() {
offsets.push((offset, unit.address_size(), low_pc));
}
}
}
}
b.iter(|| for &(offset, address_size, base_address) in &*offsets {
let mut locs = debug_loc
.locations(offset, address_size, base_address)
.expect("Should parse locations OK");
while let Some(loc) = locs.next().expect("Should parse next location") {
test::black_box(loc);
}
});
}
#[bench]
fn bench_parsing_debug_ranges(b: &mut test::Bencher) {
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_ranges = read_section("debug_ranges");
let debug_ranges = DebugRanges::new(&debug_ranges, LittleEndian);
let mut offsets = Vec::new();
let mut iter = debug_info.units();
while let Some(unit) = iter.next().expect("Should parse compilation unit") {
let abbrevs = unit.abbreviations(&debug_abbrev)
.expect("Should parse abbreviations");
let mut cursor = unit.entries(&abbrevs);
cursor.next_dfs().expect("Should parse next dfs");
let mut low_pc = 0;
{
let unit_entry = cursor.current().expect("Should have a root entry");
let low_pc_attr = unit_entry
.attr_value(gimli::DW_AT_low_pc)
.expect("Should parse low_pc");
if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
low_pc = address;
}
}
while cursor.next_dfs().expect("Should parse next dfs").is_some() {
let entry = cursor.current().expect("Should have a current entry");
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
if let gimli::AttributeValue::DebugRangesRef(offset) = attr.value() {
offsets.push((offset, unit.address_size(), low_pc));
}
}
}
}
b.iter(|| for &(offset, address_size, base_address) in &*offsets {
let mut ranges = debug_ranges
.ranges(offset, address_size, base_address)
.expect("Should parse ranges OK");
while let Some(range) = ranges.next().expect("Should parse next range") {
test::black_box(range);
}
});
}
fn debug_info_expressions<R: Reader>(
debug_info: &DebugInfo<R>,
debug_abbrev: &DebugAbbrev<R>,
) -> Vec<(Expression<R>, u8, Format)> {
let mut expressions = Vec::new();
let mut iter = debug_info.units();
while let Some(unit) = iter.next().expect("Should parse compilation unit") {
let abbrevs = unit.abbreviations(debug_abbrev)
.expect("Should parse abbreviations");
let mut cursor = unit.entries(&abbrevs);
while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") {
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
if let AttributeValue::Exprloc(expression) = attr.value() {
expressions.push((expression, unit.address_size(), unit.format()));
}
}
}
}
expressions
}
#[bench]
fn bench_parsing_debug_info_expressions(b: &mut test::Bencher) {
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let expressions = debug_info_expressions(&debug_info, &debug_abbrev);
b.iter(|| {
for &(expression, address_size, format) in &*expressions {
let mut pc = expression.0;
while !pc.is_empty() {
Operation::parse(&mut pc, &expression.0, address_size, format)
.expect("Should parse operation");
}
}
});
}
#[bench]
fn bench_evaluating_debug_info_expressions(b: &mut test::Bencher) {
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let expressions = debug_info_expressions(&debug_info, &debug_abbrev);
b.iter(|| {
for &(expression, address_size, format) in &*expressions {
let mut eval = expression.evaluation(address_size, format);
eval.set_initial_value(0);
let result = eval.evaluate().expect("Should evaluate expression");
test::black_box(result);
}
});
}
fn debug_loc_expressions<R: Reader>(
debug_info: &DebugInfo<R>,
debug_abbrev: &DebugAbbrev<R>,
debug_loc: &DebugLoc<R>,
) -> Vec<(Expression<R>, u8, Format)> {
let mut expressions = Vec::new();
let mut iter = debug_info.units();
while let Some(unit) = iter.next().expect("Should parse compilation unit") {
let abbrevs = unit.abbreviations(debug_abbrev)
.expect("Should parse abbreviations");
let mut cursor = unit.entries(&abbrevs);
cursor.next_dfs().expect("Should parse next dfs");
let mut low_pc = 0;
{
let unit_entry = cursor.current().expect("Should have a root entry");
let low_pc_attr = unit_entry
.attr_value(gimli::DW_AT_low_pc)
.expect("Should parse low_pc");
if let Some(gimli::AttributeValue::Addr(address)) = low_pc_attr {
low_pc = address;
}
}
while cursor.next_dfs().expect("Should parse next dfs").is_some() {
let entry = cursor.current().expect("Should have a current entry");
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
if let gimli::AttributeValue::DebugLocRef(offset) = attr.value() {
let mut locs = debug_loc
.locations(offset, unit.address_size(), low_pc)
.expect("Should parse locations OK");
while let Some(loc) = locs.next().expect("Should parse next location") {
expressions.push((loc.data, unit.address_size(), unit.format()));
}
}
}
}
}
expressions
}
#[bench]
fn bench_parsing_debug_loc_expressions(b: &mut test::Bencher) {
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_loc = read_section("debug_loc");
let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
let expressions = debug_loc_expressions(&debug_info, &debug_abbrev, &debug_loc);
b.iter(|| {
for &(expression, address_size, format) in &*expressions {
let mut pc = expression.0;
while !pc.is_empty() {
Operation::parse(&mut pc, &expression.0, address_size, format)
.expect("Should parse operation");
}
}
});
}
#[bench]
fn bench_evaluating_debug_loc_expressions(b: &mut test::Bencher) {
let debug_info = read_section("debug_info");
let debug_info = DebugInfo::new(&debug_info, LittleEndian);
let debug_abbrev = read_section("debug_abbrev");
let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian);
let debug_loc = read_section("debug_loc");
let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
let expressions = debug_loc_expressions(&debug_info, &debug_abbrev, &debug_loc);
b.iter(|| {
for &(expression, address_size, format) in &*expressions {
let mut eval = expression.evaluation(address_size, format);
eval.set_initial_value(0);
let result = eval.evaluate().expect("Should evaluate expression");
test::black_box(result);
}
});
}
#[cfg(target_pointer_width = "64")]
mod cfi {
extern crate fallible_iterator;
extern crate gimli;
extern crate test;
use super::*;
use self::fallible_iterator::FallibleIterator;
use gimli::{BaseAddresses, CieOrFde, EhFrame, FrameDescriptionEntry, LittleEndian,
UninitializedUnwindContext, UnwindSection, UnwindTable};
#[bench]
fn iterate_entries_and_do_not_parse_any_fde(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let bases = BaseAddresses::default().set_cfi(0).set_data(0).set_text(0);
b.iter(|| {
let mut entries = eh_frame.entries(&bases);
while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
test::black_box(entry);
}
});
}
#[bench]
fn iterate_entries_and_parse_every_fde(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let bases = BaseAddresses::default().set_cfi(0).set_data(0).set_text(0);
b.iter(|| {
let mut entries = eh_frame.entries(&bases);
while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
match entry {
CieOrFde::Cie(cie) => {
test::black_box(cie);
}
CieOrFde::Fde(partial) => {
let fde = partial
.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
.expect("Should be able to get CIE for FED");
test::black_box(fde);
}
};
}
});
}
#[bench]
fn iterate_entries_and_parse_every_fde_and_instructions(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let bases = BaseAddresses::default().set_cfi(0).set_data(0).set_text(0);
b.iter(|| {
let mut entries = eh_frame.entries(&bases);
while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
match entry {
CieOrFde::Cie(cie) => {
let mut instrs = cie.instructions();
while let Some(i) =
instrs.next().expect("Can parse next CFI instruction OK")
{
test::black_box(i);
}
}
CieOrFde::Fde(partial) => {
let fde = partial
.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
.expect("Should be able to get CIE for FED");
let mut instrs = fde.instructions();
while let Some(i) =
instrs.next().expect("Can parse next CFI instruction OK")
{
test::black_box(i);
}
}
};
}
});
}
#[bench]
fn iterate_entries_evaluate_every_fde(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let bases = BaseAddresses::default().set_cfi(0).set_data(0).set_text(0);
let mut ctx = Some(UninitializedUnwindContext::new());
b.iter(|| {
let mut entries = eh_frame.entries(&bases);
while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
match entry {
CieOrFde::Cie(_) => {}
CieOrFde::Fde(partial) => {
let fde = partial
.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
.expect("Should be able to get CIE for FED");
let mut context = ctx.take()
.unwrap()
.initialize(fde.cie())
.expect("Should be able to initialize ctx");
{
let mut table = UnwindTable::new(&mut context, &fde);
while let Some(row) =
table.next_row().expect("Should get next unwind table row")
{
test::black_box(row);
}
}
ctx = Some(context.reset());
}
};
}
});
}
fn instrs_len<R: Reader>(fde: &FrameDescriptionEntry<EhFrame<R>, R, R::Offset>) -> usize {
fde.instructions()
.fold(0, |count, _| count + 1)
.expect("fold over instructions OK")
}
fn get_fde_with_longest_cfi_instructions<R: Reader>(
eh_frame: &EhFrame<R>,
) -> FrameDescriptionEntry<EhFrame<R>, R, R::Offset> {
let bases = BaseAddresses::default().set_cfi(0).set_data(0).set_text(0);
let mut longest: Option<(usize, FrameDescriptionEntry<_, _, _>)> = None;
let mut entries = eh_frame.entries(&bases);
while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
match entry {
CieOrFde::Cie(_) => {}
CieOrFde::Fde(partial) => {
let fde = partial
.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
.expect("Should be able to get CIE for FED");
let this_len = instrs_len(&fde);
let found_new_longest = match longest {
None => true,
Some((longest_len, ref _fde)) => this_len > longest_len,
};
if found_new_longest {
longest = Some((this_len, fde));
}
}
};
}
longest.expect("At least one FDE in .eh_frame").1
}
#[bench]
fn parse_longest_fde_instructions(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let fde = get_fde_with_longest_cfi_instructions(&eh_frame);
b.iter(|| {
let mut instrs = fde.instructions();
while let Some(i) = instrs.next().expect("Should parse instruction OK") {
test::black_box(i);
}
});
}
#[bench]
fn eval_longest_fde_instructions_new_ctx_everytime(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let fde = get_fde_with_longest_cfi_instructions(&eh_frame);
b.iter(|| {
let mut ctx = UninitializedUnwindContext::new()
.initialize(fde.cie())
.expect("Should initialize the ctx OK");
let mut table = UnwindTable::new(&mut ctx, &fde);
while let Some(row) = table.next_row().expect("Should get next unwind table row") {
test::black_box(row);
}
});
}
#[bench]
fn eval_longest_fde_instructions_same_ctx(b: &mut test::Bencher) {
let eh_frame = read_section("eh_frame");
let eh_frame = EhFrame::new(&eh_frame, LittleEndian);
let fde = get_fde_with_longest_cfi_instructions(&eh_frame);
let mut ctx = Some(UninitializedUnwindContext::new());
b.iter(|| {
let mut context = ctx.take()
.unwrap()
.initialize(fde.cie())
.expect("Should be able to initialize ctx");
{
let mut table = UnwindTable::new(&mut context, &fde);
while let Some(row) = table.next_row().expect("Should get next unwind table row") {
test::black_box(row);
}
}
ctx = Some(context.reset());
});
}
}