Try to add basic performance testing (#8)
This commit is contained in:
parent
6b55222a4b
commit
d955c61cd6
76
.github/workflows/performance-tests.yml
vendored
Normal file
76
.github/workflows/performance-tests.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
name: performance-test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [g++-11, clang++-14]
|
||||
target: [Debug]
|
||||
std: [11, 20]
|
||||
config: [
|
||||
"-DCPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE=On",
|
||||
"-DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF4=On",
|
||||
"-DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On"
|
||||
]
|
||||
exclude:
|
||||
- config: -DCPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE=On -DCPPTRACE_BUILD_SPEEDTEST_DWARF5=On
|
||||
compiler: g++-11
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: sudo apt install gcc-11 g++-11 libgcc-11-dev
|
||||
- name: build
|
||||
run: |
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.target}} \
|
||||
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} \
|
||||
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
|
||||
${{matrix.config}} \
|
||||
-DCPPTRACE_BACKTRACE_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/include/backtrace.h \
|
||||
-DCPPTRACE_BUILD_SPEEDTEST=On \
|
||||
-DBUILD_SHARED_LIBS=On
|
||||
make
|
||||
- name: test
|
||||
working-directory: build
|
||||
run: |
|
||||
./speedtest | python3 ../test/speedtest.py ${{matrix.config}}
|
||||
# TODO: For some reason this is slow on github's runner
|
||||
#test-windows:
|
||||
# runs-on: windows-2019
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# compiler: [cl, clang++]
|
||||
# target: [Debug]
|
||||
# std: [11, 20]
|
||||
# config: [
|
||||
# "-DCPPTRACE_GET_SYMBOLS_WITH_DBGHELP=On"
|
||||
# ]
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Enable Developer Command Prompt
|
||||
# uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
# - name: build
|
||||
# run: |
|
||||
# mkdir -p build
|
||||
# cd build
|
||||
# cmake .. `
|
||||
# -DCMAKE_BUILD_TYPE=Debug `
|
||||
# -DCMAKE_CXX_COMPILER=${{matrix.compiler}} `
|
||||
# -DCMAKE_CXX_STANDARD=${{matrix.std}} `
|
||||
# ${{matrix.config}} `
|
||||
# -DCPPTRACE_BUILD_SPEEDTEST=On `
|
||||
# -DBUILD_SHARED_LIBS=On
|
||||
# msbuild .\cpptrace.sln
|
||||
# - name: test
|
||||
# working-directory: build
|
||||
# run: |
|
||||
# .\${{matrix.target}}\speedtest.exe | python3 ../test/speedtest.py ${{matrix.config}}
|
||||
@ -67,6 +67,10 @@ option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF)
|
||||
option(CPPTRACE_BUILD_TEST "" OFF)
|
||||
option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF)
|
||||
|
||||
option(CPPTRACE_BUILD_SPEEDTEST "" OFF)
|
||||
option(CPPTRACE_BUILD_SPEEDTEST_DWARF4 "" OFF)
|
||||
option(CPPTRACE_BUILD_SPEEDTEST_DWARF5 "" OFF)
|
||||
|
||||
set(CPPTRACE_BACKTRACE_PATH "" CACHE STRING "Path to backtrace.h, if the compiler doesn't already know it. Check /usr/lib/gcc/x86_64-linux-gnu/*/include.")
|
||||
set(CPPTRACE_HARD_MAX_FRAMES "" CACHE STRING "Hard limit on unwinding depth. Default is 100.")
|
||||
|
||||
@ -371,3 +375,47 @@ if(CPPTRACE_BUILD_TEST)
|
||||
set_property(TARGET test PROPERTY ENABLE_EXPORTS ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CPPTRACE_BUILD_SPEEDTEST)
|
||||
if(CPPTRACE_BUILD_SPEEDTEST_DWARF4)
|
||||
check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4)
|
||||
if(HAS_DWARF4)
|
||||
add_compile_options("$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||
#target_compile_options(speedtest PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||
#target_compile_options(googletest INTERFACE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||
endif()
|
||||
endif()
|
||||
if(CPPTRACE_BUILD_SPEEDTEST_DWARF5)
|
||||
check_cxx_compiler_flag("-gdwarf-5" HAS_DWARF5)
|
||||
if(HAS_DWARF5)
|
||||
add_compile_options("$<$<CONFIG:Debug>:-gdwarf-5>")
|
||||
#target_compile_options(speedtest PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||
#target_compile_options(googletest INTERFACE "$<$<CONFIG:Debug>:-gdwarf-4>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||
)
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
|
||||
add_executable(speedtest test/speedtest.cpp)
|
||||
target_link_libraries(
|
||||
speedtest
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
cpptrace
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
add_custom_command(
|
||||
TARGET speedtest POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:speedtest> $<TARGET_FILE_DIR:speedtest>
|
||||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
[](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/build.yml)
|
||||
[](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/test.yml)
|
||||
[](https://github.com/jeremy-rifkin/cpptrace/actions/workflows/performance-tests.yml)
|
||||
|
||||
🚧 WIP 🏗️
|
||||
|
||||
|
||||
@ -44,6 +44,22 @@ namespace cpptrace {
|
||||
return frames;
|
||||
}
|
||||
|
||||
bool has_addr2line() {
|
||||
// Detects if addr2line exists by trying to invoke addr2line --help
|
||||
constexpr int magic = 42;
|
||||
pid_t pid = fork();
|
||||
if(pid == -1) { return false; }
|
||||
if(pid == 0) { // child
|
||||
close(STDOUT_FILENO);
|
||||
// TODO: path
|
||||
execlp("addr2line", "addr2line", "--help", nullptr);
|
||||
exit(magic);
|
||||
}
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
return WEXITSTATUS(status) == 0;
|
||||
}
|
||||
|
||||
struct pipe_t {
|
||||
union {
|
||||
struct {
|
||||
@ -93,51 +109,53 @@ namespace cpptrace {
|
||||
struct symbolizer::impl {
|
||||
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
|
||||
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
|
||||
std::vector<dlframe> dlframes = backtrace_frames(frames);
|
||||
std::unordered_map<
|
||||
std::string,
|
||||
std::vector<std::pair<std::string, std::reference_wrapper<stacktrace_frame>>>
|
||||
> entries;
|
||||
for(size_t i = 0; i < dlframes.size(); i++) {
|
||||
const auto& entry = dlframes[i];
|
||||
entries[entry.obj_path].push_back({
|
||||
to_hex(entry.raw_address - entry.obj_base),
|
||||
trace[i]
|
||||
});
|
||||
// Set what is known for now, and resolutions from addr2line should overwrite
|
||||
trace[i].filename = entry.obj_path;
|
||||
trace[i].symbol = entry.symbol;
|
||||
}
|
||||
for(const auto& entry : entries) {
|
||||
const auto& object_name = entry.first;
|
||||
const auto& entries_vec = entry.second;
|
||||
std::string address_input;
|
||||
for(const auto& pair : entries_vec) {
|
||||
address_input += pair.first;
|
||||
address_input += '\n';
|
||||
if(has_addr2line()) {
|
||||
std::vector<dlframe> dlframes = backtrace_frames(frames);
|
||||
std::unordered_map<
|
||||
std::string,
|
||||
std::vector<std::pair<std::string, std::reference_wrapper<stacktrace_frame>>>
|
||||
> entries;
|
||||
for(size_t i = 0; i < dlframes.size(); i++) {
|
||||
const auto& entry = dlframes[i];
|
||||
entries[entry.obj_path].push_back({
|
||||
to_hex(entry.raw_address - entry.obj_base),
|
||||
trace[i]
|
||||
});
|
||||
// Set what is known for now, and resolutions from addr2line should overwrite
|
||||
trace[i].filename = entry.obj_path;
|
||||
trace[i].symbol = entry.symbol;
|
||||
}
|
||||
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
|
||||
internal_verify(output.size() == entries_vec.size());
|
||||
for(size_t i = 0; i < output.size(); i++) {
|
||||
// result will be of the form <identifier> " at " path:line
|
||||
// path may be ?? if addr2line cannot resolve, line may be ?
|
||||
const auto& line = output[i];
|
||||
auto at_location = line.find(" at ");
|
||||
internal_verify(at_location != std::string::npos);
|
||||
auto symbol = line.substr(0, at_location);
|
||||
auto colon = line.rfind(":");
|
||||
internal_verify(colon != std::string::npos);
|
||||
internal_verify(colon > at_location);
|
||||
auto filename = line.substr(at_location + 4, colon - at_location - 4);
|
||||
auto line_number = line.substr(colon + 1);
|
||||
if(line_number != "?") {
|
||||
entries_vec[i].second.get().line = std::stoi(line_number);
|
||||
for(const auto& entry : entries) {
|
||||
const auto& object_name = entry.first;
|
||||
const auto& entries_vec = entry.second;
|
||||
std::string address_input;
|
||||
for(const auto& pair : entries_vec) {
|
||||
address_input += pair.first;
|
||||
address_input += '\n';
|
||||
}
|
||||
if(filename != "??") {
|
||||
entries_vec[i].second.get().filename = filename;
|
||||
}
|
||||
if(symbol != "") {
|
||||
entries_vec[i].second.get().symbol = symbol;
|
||||
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
|
||||
internal_verify(output.size() == entries_vec.size());
|
||||
for(size_t i = 0; i < output.size(); i++) {
|
||||
// result will be of the form <identifier> " at " path:line
|
||||
// path may be ?? if addr2line cannot resolve, line may be ?
|
||||
const auto& line = output[i];
|
||||
auto at_location = line.find(" at ");
|
||||
internal_verify(at_location != std::string::npos);
|
||||
auto symbol = line.substr(0, at_location);
|
||||
auto colon = line.rfind(":");
|
||||
internal_verify(colon != std::string::npos);
|
||||
internal_verify(colon > at_location);
|
||||
auto filename = line.substr(at_location + 4, colon - at_location - 4);
|
||||
auto line_number = line.substr(colon + 1);
|
||||
if(line_number != "?") {
|
||||
entries_vec[i].second.get().line = std::stoi(line_number);
|
||||
}
|
||||
if(filename != "??") {
|
||||
entries_vec[i].second.get().filename = filename;
|
||||
}
|
||||
if(symbol != "") {
|
||||
entries_vec[i].second.get().symbol = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
test/speedtest.cpp
Normal file
11
test/speedtest.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
// https://github.com/jeremy-rifkin/libassert/issues/43
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <exception>
|
||||
|
||||
TEST(TraceTest, trace_test) {
|
||||
ASSERT_THROW((cpptrace::print_trace(), false), std::logic_error);
|
||||
}
|
||||
33
test/speedtest.py
Normal file
33
test/speedtest.py
Normal file
@ -0,0 +1,33 @@
|
||||
import sys
|
||||
import re
|
||||
|
||||
def main():
|
||||
output = sys.stdin.read()
|
||||
|
||||
print(output)
|
||||
|
||||
print("-" * 50)
|
||||
|
||||
time = int(re.search(r"\d+ tests? from \d+ test suites? ran. \((\d+) ms total\)", output).group(1))
|
||||
|
||||
dwarf4 = any(["DWARF4" in arg for arg in sys.argv[1:]])
|
||||
dwarf5 = any(["DWARF5" in arg for arg in sys.argv[1:]])
|
||||
expect_slow = dwarf4
|
||||
|
||||
threshold = 100 # ms
|
||||
|
||||
if expect_slow:
|
||||
if time > 100:
|
||||
print(f"Success (expecting slow): Test program took {time} ms")
|
||||
else:
|
||||
print(f"Error (expecting slow): Test program took {time} ms")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if time > 100:
|
||||
print(f"Error: Test program took {time} ms")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"Success: Test program took {time} ms")
|
||||
|
||||
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user