From 6a200443106bb83c6261c64c323ceb9f0563fdad Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Tue, 18 Jul 2023 18:39:00 +0200 Subject: implement flame graph generation --- test/CMakeLists.txt | 9 +++++++++ test/py/conftest.py | 41 +++++++++++++++++++++++++++++++++++++---- test/py/lib/tests.py | 4 +++- test/py/test_repo.py | 8 ++++++-- test/pytest.ini | 1 + 5 files changed, 56 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7f4ff11..d370c53 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,3 +29,12 @@ add_python_tests(python_tests_stress add_python_tests(python_tests_valgrind Python3::Interpreter -m pytest ${python_test_args} -m "not stress" --valgrind-binary "${CMAKE_CURRENT_SOURCE_DIR}/../src/valgrind.sh") + +if(NOT DEFINED FLAME_GRAPHS_DIR) + set(FLAME_GRAPHS_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +endif() + +add_python_tests(python_tests_perf + Python3::Interpreter -m pytest ${python_test_args} -m "flame_graph" + --flame-graph-binary "${CMAKE_CURRENT_SOURCE_DIR}/../scripts/flame_graph.sh" + --flame-graphs-dir "${FLAME_GRAPHS_DIR}") diff --git a/test/py/conftest.py b/test/py/conftest.py index ab2cbfb..db3f0b8 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -51,6 +51,9 @@ BINARY_PARAMS = [ PARAM_VALGRIND = ParamBinary('valgrind', required=False) +PARAM_FLAME_GRAPH = ParamBinary('flame_graph', required=False) +PARAM_FLAME_GRAPHS_DIR = Param('flame_graphs_dir', 'directory to store flame graphs', required=False) + class ParamVersion(Param): def __init__(self): @@ -62,6 +65,8 @@ PARAM_VERSION = ParamVersion() PARAMS = list(BINARY_PARAMS) PARAMS += [ PARAM_VALGRIND, + PARAM_FLAME_GRAPH, + PARAM_FLAME_GRAPHS_DIR, PARAM_VERSION, ] @@ -94,14 +99,19 @@ def paths(pytestconfig): return Paths(pytestconfig) +class CmdLineValgrind(CmdLine): + def __init__(self, binary): + # Signal to Valgrind that ci scripts should obviously be exempt from + # memory leak checking: + super().__init__(binary, '--trace-children-skip=*/ci', '--') + + @fixture(scope='session') def base_cmd_line(pytestconfig): cmd_line = CmdLine.unbuffered() valgrind = pytestconfig.getoption(PARAM_VALGRIND.codename) if valgrind is not None: - # Signal to Valgrind that ci scripts should obviously be exempt from - # memory leak checking: - cmd_line = CmdLine.wrap(CmdLine(valgrind, '--trace-children-skip=*/ci', '--'), cmd_line) + cmd_line = CmdLine.wrap(CmdLineValgrind(valgrind), cmd_line) return cmd_line @@ -194,6 +204,29 @@ def repo_path(tmp_path): return os.path.join(tmp_path, 'repo') +@fixture +def flame_graph_path(pytestconfig, tmp_path): + dir = pytestconfig.getoption(PARAM_FLAME_GRAPHS_DIR.codename) + if dir is None: + return os.path.join(tmp_path, 'flame_graph.svg') + os.makedirs(dir, exist_ok=True) + return os.path.join(dir, 'flame_graph.svg') + + +@fixture +def profiler(pytestconfig, server, workers, flame_graph_path): + script = pytestconfig.getoption(PARAM_FLAME_GRAPH.codename) + if script is None: + yield + return + pids = [server.pid] + [worker.pid for worker in workers] + pids = map(str, pids) + cmd_line = CmdLine(script, flame_graph_path, *pids) + with cmd_line.run_async() as proc: + yield + assert proc.returncode == 0 + + ALL_REPOS = [ repo.TestRepoOutputSimple, repo.TestRepoOutputEmpty, @@ -225,5 +258,5 @@ Env = namedtuple('Env', ['server', 'workers', 'client', 'db']) @fixture -def env(server, workers, client, sqlite_db): +def env(server, workers, profiler, client, sqlite_db): return Env(server, workers, client, sqlite_db) diff --git a/test/py/lib/tests.py b/test/py/lib/tests.py index a676021..fe90f96 100644 --- a/test/py/lib/tests.py +++ b/test/py/lib/tests.py @@ -14,8 +14,10 @@ def my_parametrize(names, values, ids=None, **kwargs): if len(_names) == 1: ids = [f'{names}={v}' for v in values] else: + _values = [combination.values if hasattr(combination, 'values') else combination + for combination in values] ids = [ '-'.join(f'{k}={v}' for k, v in zip(_names, combination)) - for combination in values + for combination in _values ] return pytest.mark.parametrize(names, values, ids=ids, **kwargs) diff --git a/test/py/test_repo.py b/test/py/test_repo.py index 49d5a32..c973e37 100644 --- a/test/py/test_repo.py +++ b/test/py/test_repo.py @@ -79,7 +79,11 @@ def test_repo(env, test_repo, numof_clients, runs_per_client): @pytest.mark.stress -@my_parametrize(('numof_clients', 'runs_per_client'), - [(10, 50), (1, 2000), (4, 500)]) +@my_parametrize('numof_clients,runs_per_client', + [ + (10, 50), + (1, 2000), + pytest.param(4, 500, marks=pytest.mark.flame_graph), + ]) def test_repo_stress(env, stress_test_repo, numof_clients, runs_per_client): _test_repo_internal(env, stress_test_repo, numof_clients, runs_per_client) diff --git a/test/pytest.ini b/test/pytest.ini index 766e409..49e206d 100644 --- a/test/pytest.ini +++ b/test/pytest.ini @@ -6,3 +6,4 @@ log_cli_level = INFO markers = stress: Big tests; don't run them w/ Valgrind or in QEMU + flame_graph: Generate the flame graph for these tests -- cgit v1.2.3