Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flamegraph generation in CI build #4638

Merged
merged 2 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }}
10 changes: 5 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 Down Expand Up @@ -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*)
Expand All @@ -100,4 +100,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
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 ());
}));
}
Loading