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 script to generate flamegraphs #17

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ compile_commands.json
make
test-verify
bench_utreexo
profiling_demo

# flamegraphs
*.stacks
*.folded
*.svg

# autoreconf
Makefile
Expand Down
13 changes: 12 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ AM_LDFLAGS = $(SANITIZER_LDFLAGS)

include sources.mk

bin_PROGRAMS =

#include_HEADERS = $(UTREEXO_DIST_HEADER_INT)
#include_HEADERS = $(UTREEXO_DIST_HEADER_INT) $(UTREEXO_LIB_HEADERS_INT)

Expand Down Expand Up @@ -51,10 +53,19 @@ fuzz_LDADD = $(LIBUTREEXO)
fuzz_LDFLAGS = $(AM_LDFLAGS)

if ENABLE_BENCH
bin_PROGRAMS = bench_utreexo
bin_PROGRAMS += bench_utreexo
bench_utreexo_SOURCES = ${UTREEXO_BENCH_SOURCES_INT}
bench_utreexo_CPPFLAGS = -I$(srcdir)/src $(AM_CPPFLAGS)
bench_utreexo_CXXFLAGS = $(AM_CXXFLAGS)
bench_utreexo_LDADD = $(LIBUTREEXO)
bench_utreexo_LDFLAGS = $(AM_LDFLAGS)
endif

if ENABLE_PROFILING
bin_PROGRAMS += profiling_demo
profiling_demo_SOURCES = ${UTREEXO_PROFILING_DEMO_SOURCES_INT}
profiling_demo_CPPFLAGS = -I$(srcdir)/src $(AM_CPPFLAGS)
profiling_demo_CXXFLAGS = $(AM_CXXFLAGS)
profiling_demo_LDADD = $(LIBUTREEXO)
profiling_demo_LDFLGS = $(AM_LDFLAGS)
endif
17 changes: 17 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ AC_ARG_ENABLE(bench,
[use_bench=$enableval],
[use_bench=yes])

AC_ARG_ENABLE(profiling-demo,
AS_HELP_STRING([--enable-profiling-demo],[build profiling demo (default is no).]),
[enable_profiling=$enableval],
[enable_profiling=no])

AC_ARG_ENABLE([fuzz],
AS_HELP_STRING([--enable-fuzz],
[build for fuzzing (default no). enabling this will disable all other targets.]),
Expand Down Expand Up @@ -175,6 +180,18 @@ AC_SUBST(SANITIZER_LDFLAGS)
AC_SUBST(SANITIZER_CXXFLAGS)
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
AM_CONDITIONAL([ENABLE_BENCH], [test "$use_bench" = "yes"])
AM_CONDITIONAL([ENABLE_PROFILING], [test "$enable_profiling" = "yes"])
AM_CONDITIONAL([ENABLE_FUZZ], [test x"$enable_fuzz" != x"no"])
AC_OUTPUT

echo
echo "Options used to compile:"
if test $enable_fuzz = "no"; then
echo " with test = $use_tests"
else
echo " with test = not building test_verify because fuzzing is enabled"
fi
echo " with bench = $use_bench"
echo " debug enabled = $enable_debug"
echo " profiling demo = $enable_profiling"
echo
71 changes: 71 additions & 0 deletions generate_flamegraph
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash

# This script generates flame graphs by using DTrace to sample the active stack frames of the
# specified process and then https://github.com/brendangregg/FlameGraph to convert the
# stack traces into the flame graph.
# Note: On all current MacOS versions System Integrity Protection (SIP) is enabled by default and
# prevents most uses of dtrace. The usual way to make dtrace work on MacOS is to boot into
# recovery mode and disable some of the SIP protections with `csrutil enable --without dtrace`

# The specified process is located at `src/profiling_demo/profiling_demo.cpp` and is build by using
# the --enable-profiling-demo flag. `profiling_demo` is the default process but there is also the
# option to generate flame graphs for individual benchmarks using
# `./generate_flamegraph <durations(seconds)> bench_utreexo <benchmark-name>`

# this is based off some work by fanquake: https://github.com/fanquake/core-review/tree/master/flamegraph
# NOTE: for this to work, you need to clone the Flame Graph repo and set the appropriate variable
ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO="<ADD_PATH>"

OUTPUT_FOLDER="flamegraphs" #relative path to flamegraph output
DURATION=${1:-30} # profiling duration in seconds

# Determine for what process a flame graph will be generated
DEFAULT_PROCESS="profiling_demo"
SECONDARY_PROCESS="bench_utreexo"
FILE=${2:-$DEFAULT_PROCESS}
FLAMEGRAPH_TITLE="Utreexo (${FILE})"
PROCESS_CMD="./$FILE" # the process for which profiling will run

if [ "$FILE" == $SECONDARY_PROCESS ]; then
# there is also the option to generate flamegraph for a specific benchmark
[ -z "$3" ] && {
echo "If you want to profile bench_utreexo you need to also provide the name of the benchmark"
echo "e.g ./generate_flamegraph 30 bench_utreexo RemoveElementsForest"
exit 1
}
# the profiling duration matches the minimum runtime of the benchmark
PROCESS_CMD="${PROCESS_CMD} -filter=${3}.* -min_time=$((DURATION * 1000))"
FLAMEGRAPH_TITLE="${FLAMEGRAPH_TITLE} ${3}"
fi

NOW=$(date '+%F_%H:%M:%S') # timestamp
OUTPUT="$(pwd)/${OUTPUT_FOLDER}/${NOW}_flamegraph"

# Verify that required files exists
[ "$FILE" == $DEFAULT_PROCESS ] && [ ! -f "$FILE" ] && {
echo "Profiling demo not found. To build use -enable-profiling-demo"
exit 1
}
[ "$FILE" == $SECONDARY_PROCESS ] && [ ! -f "$FILE" ] && {
echo "Benchmarks executable not found. Make sure to build with benchmarks enabled"
exit 1
}
[ ! -f "${ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO}/stackcollapse.pl" ] || [ ! -f "${ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO}/flamegraph.pl" ] && {
echo "The specified path for the FlameGraph repo does not contain the neccessary files"
echo "Make sure that 'ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO=${ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO}' is the correct path"
exit 1
}

# Profiling
mkdir -p $OUTPUT_FOLDER # create output directory if not exists
sudo true # make sure that we clear sudo's password prompt before we start running anything time-sensitive
$PROCESS_CMD & # run the specified process in the background
echo "Profiling of $PROCESS_CMD has started, capturing stackframes for ${DURATION}s..."
# capture $DURATION worth of stackframes using DTrace at 99 Hertz for the specified process
sudo dtrace -x ustackframes=100 -o "${OUTPUT}.stacks" -n 'profile-99 /pid == $1 && arg1/ { @[ustack()] = count(); } $2 { exit(0); }' $! "tick-${DURATION}s"
kill $! # kill profiling demo otherwise it will run for ever

# Converting stack traces into the flame graph
"${ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO}"/stackcollapse.pl "${OUTPUT}.stacks" >"${OUTPUT}.folded"
"${ABSOLUTE_PATH_TO_FLAME_GRAPH_REPO}"/flamegraph.pl "${OUTPUT}.folded" --title "$FLAMEGRAPH_TITLE" --width 1600 >"${OUTPUT}.svg"
echo "Generated flame graph can be found at $OUTPUT"
3 changes: 3 additions & 0 deletions sources.mk
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ UTREEXO_BENCH_SOURCES_INT += %reldir%/src/bench/nanobench.h
UTREEXO_BENCH_SOURCES_INT += %reldir%/src/bench/util/args.h
UTREEXO_BENCH_SOURCES_INT += %reldir%/src/bench/util/args.cpp
UTREEXO_BENCH_SOURCES_INT += %reldir%/src/bench/util/leaves.h

UTREEXO_PROFILING_DEMO_SOURCES_INT =
UTREEXO_PROFILING_DEMO_SOURCES_INT += %reldir%/src/profiling_demo/profiling_demo.cpp
36 changes: 36 additions & 0 deletions src/profiling_demo/profiling_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "bench/util/leaves.h"
#include "include/utreexo.h"

using namespace utreexo;

int main(int argc, char** argv)
{
// This is a simple profiling demo.
// The scenario that we want to profile must be written here and using
// --enable-profiling-demo it builds to the `profiling_demo` executable.
// The `generate_flamegraph` script profiles this code and
// creates the flamegraph. For more details see `generate_flamegraph`.

// ...do any setup needed...
UndoBatch unused_undo;
const int num_leaves = 105;

std::vector<Leaf> leaves;
CreateTestLeaves(leaves, num_leaves);

/**
* Flame graphs are generated by sampling the active stack frames at
* regular intervals and then converting them into a flame graph.
*
* The endless loop is to make sure that operations of interest that
* are relatively fast, will execute for enough time to end up on
* stack traces and thus on the flamegraph
*/
for (;;) {
// ...operations to be profiled...
RamForest full(0); // initialize
full.Modify(unused_undo, leaves, {}); // add leaves
}

return EXIT_SUCCESS;
}