diff --git a/benching/benchlib/DwBenchmark.cpp b/benching/benchlib/DwBenchmark.cpp new file mode 100644 index 0000000..5811f3d --- /dev/null +++ b/benching/benchlib/DwBenchmark.cpp @@ -0,0 +1,103 @@ +#include "DwBenchmark.hpp" + +#include +#include +#include + +#ifdef UNWIND_EH_ELF +#include "../../stack_walker/stack_walker.hpp" +#endif + +#ifdef UNWIND_LIBUNWIND +#include +#endif + +using namespace std; + +unique_ptr DwBenchmark::instance = nullptr; + +DwBenchmark::DwBenchmark() { +#ifdef UNWIND_EH_ELF + stack_walker_init(); +#endif +} + +DwBenchmark& DwBenchmark::get_instance() { + if(!DwBenchmark::instance) + instance = unique_ptr(new DwBenchmark); + return *instance; +} + +void DwBenchmark::unwind_measure() { +#ifdef UNWIND_EH_ELF + unwind_context_t context = get_context(); + SingleUnwind this_measure; + this_measure.nb_frames = 0; + + auto start_time = chrono::high_resolution_clock::now(); + while(unwind_context(context)) { + this_measure.nb_frames++; + } + auto end_time = chrono::high_resolution_clock::now(); + + this_measure.nanoseconds = chrono::duration_cast( + end_time - start_time).count(); + + add_measure(this_measure); +#elif UNWIND_LIBUNWIND + unw_context_t context; + int rc = unw_getcontext(&context); + if(rc < 0) + assert(false); + unw_cursor_t cursor; + rc = unw_init_local(&cursor, &context); + if(rc < 0) + assert(false); + + SingleUnwind this_measure; + this_measure.nb_frames = 0; + + auto start_time = chrono::high_resolution_clock::now(); + while(unw_step(&cursor) > 0) { + this_measure.nb_frames++; + } + auto end_time = chrono::high_resolution_clock::now(); + + this_measure.nanoseconds = chrono::duration_cast( + end_time - start_time).count(); + + add_measure(this_measure); +#else + assert(false); +#endif +} + +void DwBenchmark::add_measure(const SingleUnwind& measure) { + measures.push_back(measure); +} +void DwBenchmark::add_measure(int nb_frames, size_t microseconds) { + add_measure(SingleUnwind({nb_frames, microseconds})); +} + +void DwBenchmark::format_output(std::ostream& os) const { + size_t nb_unwind_frames = 0; + size_t total_nanoseconds = 0; + + for(const auto& measure: measures) { + nb_unwind_frames += measure.nb_frames; + total_nanoseconds += measure.nanoseconds; + } + + double clock_precision_ns = + ((double) + (1000*1000*1000 * std::chrono::high_resolution_clock::period::num)) + / ((double) std::chrono::high_resolution_clock::period::den); + + os << "Total time: " << total_nanoseconds << "ns" << endl + << "Total frames: " << nb_unwind_frames << endl + << "Avg frames/unwind: " + << (double)nb_unwind_frames / (double)measures.size() << endl + << "Avg time/frame: " + << (double)total_nanoseconds / nb_unwind_frames << "ns" << endl + << "Clock precision: " << clock_precision_ns << "ns" << endl; +} diff --git a/benching/benchlib/DwBenchmark.hpp b/benching/benchlib/DwBenchmark.hpp new file mode 100644 index 0000000..28b3c54 --- /dev/null +++ b/benching/benchlib/DwBenchmark.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +class DwBenchmark { + /** Singleton class - keeps track of benchmarks performed during a run */ + + public: + struct SingleUnwind { + int nb_frames; + size_t nanoseconds; + }; + + static DwBenchmark& get_instance(); + + void unwind_measure(); ///< Unwind from here, and add the measure + + void add_measure(const SingleUnwind& measure); ///< Add this measure + void add_measure(int nb_frames, size_t microseconds); + + /** Dump formatted output on `os` displaying stats about this benchmark + * run */ + void format_output(std::ostream& os) const; + + private: + DwBenchmark(); + + static std::unique_ptr instance; + + std::vector measures; +}; diff --git a/benching/benchlib/Makefile b/benching/benchlib/Makefile new file mode 100644 index 0000000..671f29b --- /dev/null +++ b/benching/benchlib/Makefile @@ -0,0 +1,32 @@ +TARGETS=libbench.eh_elf.so libbench.unwind.so +COMMON_OBJS=bench.o +EH_ELF_OBJS=DwBenchmark.eh_elf.o +UNWIND_OBJS=DwBenchmark.unwind.o +CXX=g++ +CXXFLAGS=-Wall -Wextra -O2 -std=c++14 +CXXLIBS= + + +all: $(TARGETS) + +libbench.eh_elf.so: $(EH_ELF_OBJS) $(COMMON_OBJS) + $(CXX) $(CXXFLAGS) -shared -o $@ $^ $(CXXLIBS) \ + -L../../stack_walker -lstack_walker.global + +libbench.unwind.so: $(UNWIND_OBJS) $(COMMON_OBJS) + $(CXX) $(CXXFLAGS) -shared -o $@ $^ $(CXXLIBS) \ + -lunwind -lunwind-x86_64 + +%.eh_elf.o: %.cpp + $(CXX) $(CXXFLAGS) -fPIC -DUNWIND_EH_ELF -o $@ -c $< + +%.unwind.o: %.cpp + $(CXX) $(CXXFLAGS) -fPIC -DUNWIND_LIBUNWIND -o $@ -c $< + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -fPIC -o $@ -c $< + + +.PHONY: clean +clean: + rm *.o $(TARGETS) diff --git a/benching/benchlib/bench.cpp b/benching/benchlib/bench.cpp new file mode 100644 index 0000000..ced0136 --- /dev/null +++ b/benching/benchlib/bench.cpp @@ -0,0 +1,12 @@ +#include "bench.h" + +#include +#include "DwBenchmark.hpp" + +void bench_unwinding() { + DwBenchmark::get_instance().unwind_measure(); +} + +void bench_dump_data() { + DwBenchmark::get_instance().format_output(std::cout); +} diff --git a/benching/benchlib/bench.h b/benching/benchlib/bench.h new file mode 100644 index 0000000..0804cc9 --- /dev/null +++ b/benching/benchlib/bench.h @@ -0,0 +1,16 @@ +/** Benchmarking library: a collection of functions that can be called to + * benchmark dwarf-assembly + **/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void bench_unwinding(); +void bench_dump_data(); + +#ifdef __cplusplus +} // END extern "C" +#endif