From 812b53bda71ebd2365c09e41cba05bdbc311e3d2 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Thu, 23 May 2024 16:32:11 +0100 Subject: [PATCH] Add flamegraph generation in CI build (#4638) Flamegraphs are attached to the build as artifacts. New tests can be added by creating a new gtest in slow_test. Tests like TEST (flamegraph, testname_x) will be executed if testname_x is added to the flamegraph.yaml file test matrix. --- .github/workflows/flamegraphs.yml | 66 +++++++++++++++++++ ci/build.sh | 10 +-- ci/tests/run-flamegraph-tests.sh | 14 ++++ ci/tests/run-tests.sh | 5 +- nano/slow_test/CMakeLists.txt | 4 +- nano/slow_test/flamegraph.cpp | 106 ++++++++++++++++++++++++++++++ 6 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/flamegraphs.yml create mode 100755 ci/tests/run-flamegraph-tests.sh create mode 100644 nano/slow_test/flamegraph.cpp diff --git a/.github/workflows/flamegraphs.yml b/.github/workflows/flamegraphs.yml new file mode 100644 index 0000000000..877b081214 --- /dev/null +++ b/.github/workflows/flamegraphs.yml @@ -0,0 +1,66 @@ +name: Code Flamegraphs + +on: [push, pull_request, workflow_dispatch] + +jobs: + linux_flamegraphs: + name: Linux [${{ matrix.TEST_NAME }}] + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + TEST_NAME: [large_confirmation, large_direct_processing] # slow_test --gtest_filter=flamegraph.[name] + runs-on: ubuntu-22.04 + env: + TEST_NAME: ${{ matrix.TEST_NAME }} + BACKEND: lmdb + COMPILER: gcc + TEST_USE_ROCKSDB: "0" + DEADLINE_SCALE_FACTOR: "1" + BUILD_TYPE: "RelWithDebInfo" + OUTPUT_FILE: ${{ matrix.TEST_NAME }}.${{ github.sha }}.svg + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: "recursive" + + - name: Prepare + run: sudo -E ci/prepare/linux/prepare.sh + + - name: Install perf and FlameGraph + run: | + sudo apt-get update + sudo apt-get install -y linux-tools-common linux-tools-generic linux-tools-$(uname -r) + git clone https://github.com/brendangregg/FlameGraph.git + export PATH=$PATH:$(pwd)/FlameGraph + + - name: Build Tests + id: build + run: ci/build-tests.sh + + - name: Run Flamegraph Tests + if: steps.build.outcome == 'success' + run: sudo perf record -F 397 -g --call-graph dwarf -o perf.data -- ../ci/tests/run-flamegraph-tests.sh ${{ matrix.TEST_NAME }} + working-directory: build + + - name: CHOWN perf.data + if: steps.build.outcome == 'success' + run: sudo chown $(whoami) perf.data + working-directory: build + + - name: Generate Flamegraph + if: steps.build.outcome == 'success' + run: | + perf script -i perf.data > out.perf + ../FlameGraph/stackcollapse-perf.pl out.perf > out.folded + ../FlameGraph/flamegraph.pl out.folded > ${{ env.OUTPUT_FILE }} + working-directory: build + + - name: Upload Flamegraph + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v3 + with: + name: flamegraph-${{ env.OUTPUT_FILE }} + path: build/${{ env.OUTPUT_FILE }} diff --git a/ci/build.sh b/ci/build.sh index 4aabc4d0fb..bee8f057a7 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -27,10 +27,10 @@ fi CMAKE_SANITIZER="" if [[ ${SANITIZER:-} ]]; then case "${SANITIZER}" in - ASAN) + ASAN) CMAKE_SANITIZER="-DNANO_ASAN=ON" ;; - ASAN_INT) + ASAN_INT) CMAKE_SANITIZER="-DNANO_ASAN_INT=ON" ;; TSAN) @@ -71,10 +71,10 @@ ${SRC} number_of_processors() { case "$(uname -s)" in - Linux*) + Linux*) nproc ;; - Darwin*) + Darwin*) sysctl -n hw.ncpu ;; CYGWIN*|MINGW32*|MSYS*|MINGW*) @@ -100,4 +100,4 @@ parallel_build_flag() { cmake --build ${PWD} ${BUILD_TARGET} $(parallel_build_flag) -popd \ No newline at end of file +popd diff --git a/ci/tests/run-flamegraph-tests.sh b/ci/tests/run-flamegraph-tests.sh new file mode 100755 index 0000000000..283fd4f8c9 --- /dev/null +++ b/ci/tests/run-flamegraph-tests.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +# Ensure that an argument is provided +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Capture the argument +ARGUMENT="$1" + +# Run the command with the argument +$(dirname "$BASH_SOURCE")/run-tests.sh slow_test --gtest_filter=flamegraph.${ARGUMENT} diff --git a/ci/tests/run-tests.sh b/ci/tests/run-tests.sh index 7ac5594e3c..7f48962359 100755 --- a/ci/tests/run-tests.sh +++ b/ci/tests/run-tests.sh @@ -47,8 +47,9 @@ case "$(uname -s)" in esac # Run the test +shift executable=./${target}$(get_exec_extension) -"${executable}" +"${executable}" "$@" status=$? if [ $status -ne 0 ]; then @@ -61,4 +62,4 @@ if [ $status -ne 0 ]; then exit $status else exit 0 -fi \ No newline at end of file +fi diff --git a/nano/slow_test/CMakeLists.txt b/nano/slow_test/CMakeLists.txt index 1483d9a5ab..a322e593a8 100644 --- a/nano/slow_test/CMakeLists.txt +++ b/nano/slow_test/CMakeLists.txt @@ -1,5 +1,5 @@ -add_executable(slow_test entry.cpp node.cpp vote_cache.cpp vote_processor.cpp - bootstrap.cpp) +add_executable(slow_test entry.cpp flamegraph.cpp node.cpp vote_cache.cpp + vote_processor.cpp bootstrap.cpp) target_link_libraries(slow_test test_common) diff --git a/nano/slow_test/flamegraph.cpp b/nano/slow_test/flamegraph.cpp new file mode 100644 index 0000000000..48bbc6eafb --- /dev/null +++ b/nano/slow_test/flamegraph.cpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +#include + +#include + +using namespace std::chrono_literals; + +namespace +{ +std::deque rep_set (size_t count) +{ + std::deque result; + for (auto i = 0; i < count; ++i) + { + result.emplace_back (nano::keypair{}); + } + return result; +} +} + +TEST (flamegraph, large_direct_processing) +{ + auto reps = rep_set (4); + auto circulating = 10 * nano::Gxrb_ratio; + nano::test::system system; + system.ledger_initialization_set (reps, circulating); + auto & node = *system.add_node (); + auto prepare = [&] () { + nano::state_block_builder builder; + std::deque> blocks; + std::deque keys; + auto previous = *std::prev (std::prev (system.initialization_blocks.end ())); + for (auto i = 0; i < 20000; ++i) + { + keys.emplace_back (); + auto const & key = keys.back (); + auto block = builder.make_block () + .account (nano::dev::genesis_key.pub) + .representative (nano::dev::genesis_key.pub) + .previous (previous->hash ()) + .link (key.pub) + .balance (previous->balance_field ().value ().number () - nano::xrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (previous->hash ())) + .build (); + blocks.push_back (block); + previous = block; + } + return std::make_tuple (blocks, keys); + }; + auto const & [blocks, keys] = prepare (); + auto execute = [&] () { + auto count = 0; + for (auto block : blocks) + { + ASSERT_EQ (nano::block_status::progress, node.process (block)); + } + }; + execute (); +} + +TEST (flamegraph, large_confirmation) +{ + auto reps = rep_set (4); + auto circulating = 10 * nano::Gxrb_ratio; + nano::test::system system; + system.ledger_initialization_set (reps, circulating); + auto prepare = [&] () { + nano::state_block_builder builder; + std::deque> blocks; + std::deque keys; + auto previous = *std::prev (std::prev (system.initialization_blocks.end ())); + for (auto i = 0; i < 100; ++i) + { + keys.emplace_back (); + auto const & key = keys.back (); + auto block = builder.make_block () + .account (nano::dev::genesis_key.pub) + .representative (nano::dev::genesis_key.pub) + .previous (previous->hash ()) + .link (key.pub) + .balance (previous->balance_field ().value ().number () - nano::xrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (previous->hash ())) + .build (); + blocks.push_back (block); + previous = block; + } + return std::make_tuple (blocks, keys); + }; + auto const & [blocks, keys] = prepare (); + system.initialization_blocks.insert (system.initialization_blocks.end (), blocks.begin (), blocks.end ()); + nano::node_config config; + nano::node_flags flags; + auto & node1 = *system.add_node (config, flags, nano::transport::transport_type::tcp, reps[0]); + auto & node2 = *system.add_node (config, flags, nano::transport::transport_type::tcp, reps[1]); + auto & node3 = *system.add_node (config, flags, nano::transport::transport_type::tcp, reps[2]); + auto & node4 = *system.add_node (config, flags, nano::transport::transport_type::tcp, reps[3]); + ASSERT_TIMELY (300s, std::all_of (system.nodes.begin (), system.nodes.end (), [&] (auto const & node) { + return node->block_confirmed (system.initialization_blocks.back ()->hash ()); + })); +}