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.
This commit is contained in:
clemahieu 2024-05-23 16:32:11 +01:00 committed by GitHub
commit 812b53bda7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 196 additions and 9 deletions

66
.github/workflows/flamegraphs.yml vendored Normal file
View file

@ -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 }}

View file

@ -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
popd

View file

@ -0,0 +1,14 @@
#!/bin/bash
set -euo pipefail
# Ensure that an argument is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <argument>"
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}

View file

@ -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
fi

View file

@ -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)

View file

@ -0,0 +1,106 @@
#include <nano/lib/blockbuilders.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/test_common/system.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
#include <chrono>
using namespace std::chrono_literals;
namespace
{
std::deque<nano::keypair> rep_set (size_t count)
{
std::deque<nano::keypair> 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<std::shared_ptr<nano::block>> blocks;
std::deque<nano::keypair> 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<std::shared_ptr<nano::block>> blocks;
std::deque<nano::keypair> 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 ());
}));
}