Skip to content

Commit

Permalink
Add flamegraph generation in CI build
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
clemahieu committed May 23, 2024
1 parent 2ea4a06 commit 27d0a26
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 10 deletions.
66 changes: 66 additions & 0 deletions .github/workflows/flamegraphs.yml
Original file line number Diff line number Diff line change
@@ -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 }}
11 changes: 6 additions & 5 deletions ci/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -56,6 +56,7 @@ pushd $BUILD_DIR

cmake \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE:-"Debug"} \
-DCMAKE_CXX_FLAGS="-g -Og" \
-DPORTABLE=ON \
-DACTIVE_NETWORK=nano_${NANO_NETWORK:-"live"}_network \
-DNANO_TEST=${NANO_TEST:-OFF} \
Expand All @@ -71,10 +72,10 @@ ${SRC}

number_of_processors() {
case "$(uname -s)" in
Linux*)
Linux*)
nproc
;;
Darwin*)
Darwin*)
sysctl -n hw.ncpu
;;
CYGWIN*|MINGW32*|MSYS*|MINGW*)
Expand All @@ -100,4 +101,4 @@ parallel_build_flag() {

cmake --build ${PWD} ${BUILD_TARGET} $(parallel_build_flag)

popd
popd
14 changes: 14 additions & 0 deletions ci/tests/run-flamegraph-tests.sh
Original file line number Diff line number Diff line change
@@ -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}
5 changes: 3 additions & 2 deletions ci/tests/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -61,4 +62,4 @@ if [ $status -ne 0 ]; then
exit $status
else
exit 0
fi
fi
2 changes: 1 addition & 1 deletion nano/lib/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ bool running_within_valgrind ()

bool memory_intensive_instrumentation ()
{
return is_tsan_build () || nano::running_within_valgrind ();
return nano::env::get<bool> ("NANO_MEMORY_INTENSIVE") || is_tsan_build () || nano::running_within_valgrind ();
}

bool slow_instrumentation ()
Expand Down
4 changes: 2 additions & 2 deletions nano/slow_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
106 changes: 106 additions & 0 deletions nano/slow_test/flamegraph.cpp
Original file line number Diff line number Diff line change
@@ -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 ());
}));
}

0 comments on commit 27d0a26

Please sign in to comment.