#![feature(test)]

extern crate test;

use gimli::{
    AttributeValue, DebugAbbrev, DebugAddr, DebugAddrBase, DebugAranges, DebugInfo, DebugLine,
    DebugLineOffset, DebugLoc, DebugLocLists, DebugPubNames, DebugPubTypes, DebugRanges,
    DebugRngLists, Encoding, EndianSlice, EntriesTreeNode, Expression, LittleEndian, LocationLists,
    Operation, RangeLists, Reader, ReaderOffset,
};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::rc::Rc;

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"),
        );
    });
}

#[inline]
fn impl_bench_parsing_debug_info<R: Reader>(
    debug_info: DebugInfo<R>,
    debug_abbrev: DebugAbbrev<R>,
) {
    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();
            loop {
                match attrs.next() {
                    Ok(Some(ref attr)) => {
                        test::black_box(attr);
                    }
                    Ok(None) => break,
                    e @ Err(_) => {
                        e.expect("Should parse entry's attribute");
                    }
                }
            }
        }
    }
}

#[bench]
fn bench_parsing_debug_info(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);

    b.iter(|| impl_bench_parsing_debug_info(debug_info, debug_abbrev));
}

#[bench]
fn bench_parsing_debug_info_with_endian_rc_slice(b: &mut test::Bencher) {
    let debug_info = read_section("debug_info");
    let debug_info = Rc::from(&debug_info[..]);
    let debug_info = gimli::EndianRcSlice::new(debug_info, LittleEndian);
    let debug_info = DebugInfo::from(debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = Rc::from(&debug_abbrev[..]);
    let debug_abbrev = gimli::EndianRcSlice::new(debug_abbrev, LittleEndian);
    let debug_abbrev = DebugAbbrev::from(debug_abbrev);

    b.iter(|| impl_bench_parsing_debug_info(debug_info.clone(), debug_abbrev.clone()));
}

#[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();
        loop {
            match attrs.next() {
                Ok(Some(ref attr)) => {
                    test::black_box(attr);
                }
                Ok(None) => break,
                e @ Err(_) => {
                    e.expect("Should parse entry's attribute");
                }
            }
        }
    }
    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_info_raw(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 raw = unit
                .entries_raw(&abbrevs, None)
                .expect("Should have entries");
            while !raw.is_empty() {
                if let Some(abbrev) = raw
                    .read_abbreviation()
                    .expect("Should parse abbreviation code")
                {
                    for spec in abbrev.attributes().iter().cloned() {
                        match raw.read_attribute(spec) {
                            Ok(ref attr) => {
                                test::black_box(attr);
                            }
                            e @ Err(_) => {
                                e.expect("Should parse attribute");
                            }
                        }
                    }
                }
            }
        }
    });
}

#[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);
        }
    });
}

// We happen to know that there is a line number program and header at
// offset 0 and that address size is 8 bytes. No need to parse DIEs to grab
// this info off of the compilation units.
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 instructions = header.instructions();
        while let Some(instruction) = instructions
            .next_instruction(header)
            .expect("Should parse instruction")
        {
            test::black_box(instruction);
        }
    });
}

#[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_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
    let debug_addr_base = DebugAddrBase(0);

    let debug_loc = read_section("debug_loc");
    let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
    let debug_loclists = DebugLocLists::new(&[], LittleEndian);
    let loclists = LocationLists::new(debug_loc, debug_loclists);

    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::LocationListsRef(offset) = attr.value() {
                    offsets.push((offset, unit.encoding(), low_pc));
                }
            }
        }
    }

    b.iter(|| {
        for &(offset, encoding, base_address) in &*offsets {
            let mut locs = loclists
                .locations(offset, encoding, base_address, &debug_addr, debug_addr_base)
                .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_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));
    let debug_addr_base = DebugAddrBase(0);

    let debug_ranges = read_section("debug_ranges");
    let debug_ranges = DebugRanges::new(&debug_ranges, LittleEndian);
    let debug_rnglists = DebugRngLists::new(&[], LittleEndian);
    let rnglists = RangeLists::new(debug_ranges, debug_rnglists);

    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::RangeListsRef(offset) = attr.value() {
                    offsets.push((offset, unit.encoding(), low_pc));
                }
            }
        }
    }

    b.iter(|| {
        for &(offset, encoding, base_address) in &*offsets {
            let mut ranges = rnglists
                .ranges(offset, encoding, base_address, &debug_addr, debug_addr_base)
                .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>, Encoding)> {
    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.encoding()));
                }
            }
        }
    }

    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, encoding) in &*expressions {
            let mut pc = expression.0;
            while !pc.is_empty() {
                Operation::parse(&mut pc, encoding).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, encoding) in &*expressions {
            let mut eval = expression.evaluation(encoding);
            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_addr: &DebugAddr<R>,
    loclists: &LocationLists<R>,
) -> Vec<(Expression<R>, Encoding)> {
    let debug_addr_base = DebugAddrBase(R::Offset::from_u8(0));

    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::LocationListsRef(offset) = attr.value() {
                    let mut locs = loclists
                        .locations(offset, unit.encoding(), low_pc, debug_addr, debug_addr_base)
                        .expect("Should parse locations OK");
                    while let Some(loc) = locs.next().expect("Should parse next location") {
                        expressions.push((loc.data, unit.encoding()));
                    }
                }
            }
        }
    }

    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_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));

    let debug_loc = read_section("debug_loc");
    let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
    let debug_loclists = DebugLocLists::new(&[], LittleEndian);
    let loclists = LocationLists::new(debug_loc, debug_loclists);

    let expressions = debug_loc_expressions(&debug_info, &debug_abbrev, &debug_addr, &loclists);

    b.iter(|| {
        for &(expression, encoding) in &*expressions {
            let mut pc = expression.0;
            while !pc.is_empty() {
                Operation::parse(&mut pc, encoding).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_addr = DebugAddr::from(EndianSlice::new(&[], LittleEndian));

    let debug_loc = read_section("debug_loc");
    let debug_loc = DebugLoc::new(&debug_loc, LittleEndian);
    let debug_loclists = DebugLocLists::new(&[], LittleEndian);
    let loclists = LocationLists::new(debug_loc, debug_loclists);

    let expressions = debug_loc_expressions(&debug_info, &debug_abbrev, &debug_addr, &loclists);

    b.iter(|| {
        for &(expression, encoding) in &*expressions {
            let mut eval = expression.evaluation(encoding);
            eval.set_initial_value(0);
            let result = eval.evaluate().expect("Should evaluate expression");
            test::black_box(result);
        }
    });
}

// See comment above `test_parse_self_eh_frame`.
#[cfg(target_pointer_width = "64")]
mod cfi {
    use super::*;
    use fallible_iterator::FallibleIterator;

    use gimli::{
        BaseAddresses, CieOrFde, EhFrame, FrameDescriptionEntry, LittleEndian,
        UninitializedUnwindContext, UnwindSection,
    };

    #[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_eh_frame(0)
            .set_got(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_eh_frame(0)
            .set_got(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(EhFrame::cie_from_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_eh_frame(0)
            .set_got(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(&eh_frame, &bases);
                        while let Some(i) =
                            instrs.next().expect("Can parse next CFI instruction OK")
                        {
                            test::black_box(i);
                        }
                    }
                    CieOrFde::Fde(partial) => {
                        let fde = partial
                            .parse(EhFrame::cie_from_offset)
                            .expect("Should be able to get CIE for FED");
                        let mut instrs = fde.instructions(&eh_frame, &bases);
                        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_eh_frame(0)
            .set_got(0)
            .set_text(0);

        let mut ctx = 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(EhFrame::cie_from_offset)
                            .expect("Should be able to get CIE for FED");
                        let mut table = fde
                            .rows(&eh_frame, &bases, &mut ctx)
                            .expect("Should be able to initialize ctx");
                        while let Some(row) =
                            table.next_row().expect("Should get next unwind table row")
                        {
                            test::black_box(row);
                        }
                    }
                };
            }
        });
    }

    fn instrs_len<R: Reader>(
        eh_frame: &EhFrame<R>,
        bases: &BaseAddresses,
        fde: &FrameDescriptionEntry<R>,
    ) -> usize {
        fde.instructions(eh_frame, bases)
            .fold(0, |count, _| Ok(count + 1))
            .expect("fold over instructions OK")
    }

    fn get_fde_with_longest_cfi_instructions<R: Reader>(
        eh_frame: &EhFrame<R>,
        bases: &BaseAddresses,
    ) -> FrameDescriptionEntry<R> {
        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(EhFrame::cie_from_offset)
                        .expect("Should be able to get CIE for FED");

                    let this_len = instrs_len(eh_frame, bases, &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 bases = BaseAddresses::default()
            .set_eh_frame(0)
            .set_got(0)
            .set_text(0);
        let fde = get_fde_with_longest_cfi_instructions(&eh_frame, &bases);

        b.iter(|| {
            let mut instrs = fde.instructions(&eh_frame, &bases);
            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 bases = BaseAddresses::default()
            .set_eh_frame(0)
            .set_got(0)
            .set_text(0);
        let fde = get_fde_with_longest_cfi_instructions(&eh_frame, &bases);

        b.iter(|| {
            let mut ctx = UninitializedUnwindContext::new();
            let mut table = fde
                .rows(&eh_frame, &bases, &mut ctx)
                .expect("Should initialize the ctx OK");
            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 bases = BaseAddresses::default()
            .set_eh_frame(0)
            .set_got(0)
            .set_text(0);
        let fde = get_fde_with_longest_cfi_instructions(&eh_frame, &bases);

        let mut ctx = UninitializedUnwindContext::new();

        b.iter(|| {
            let mut table = fde
                .rows(&eh_frame, &bases, &mut ctx)
                .expect("Should initialize the ctx OK");
            while let Some(row) = table.next_row().expect("Should get next unwind table row") {
                test::black_box(row);
            }
        });
    }
}
