From: Kienan Stewart Date: Thu, 7 Mar 2024 20:20:17 +0000 (-0500) Subject: tests: Split test_ust_constructor into several tests X-Git-Url: https://git.liburcu.org/?p=lttng-tools.git;a=commitdiff_plain;h=f506900f904887310c26a441ea86352fc27242c2 tests: Split test_ust_constructor into several tests Observed issue ============== TAP parsers fail when parsing a single executable that contains several plans. Eg., ``` ok 44 - Found no unexpected events PASS: ust/ust-constructor/test_ust_constructor.py 44 - Found no unexpected events 1..44 ERROR: ust/ust-constructor/test_ust_constructor.py - multiple test plans ok 1 - Create a session ERROR: ust/ust-constructor/test_ust_constructor.py 1 - Create a session # UNPLANNED ``` and ``` 14:03:23 org.tap4j.parser.ParserException: Error parsing TAP Stream: Duplicated TAP Plan found. 14:03:23 at org.tap4j.parser.Tap13Parser.parseTapStream(Tap13Parser.java:257) 14:03:23 at org.tap4j.parser.Tap13Parser.parseFile(Tap13Parser.java:231) 14:03:23 at org.tap4j.plugin.TapParser.parse(TapParser.java:172) 14:03:23 at org.tap4j.plugin.TapPublisher.loadResults(TapPublisher.java:475) 14:03:23 at org.tap4j.plugin.TapPublisher.performImpl(TapPublisher.java:352) 14:03:23 at org.tap4j.plugin.TapPublisher.perform(TapPublisher.java:312) 14:03:23 at jenkins.tasks.SimpleBuildStep.perform(SimpleBuildStep.java:123) 14:03:23 at hudson.tasks.BuildStepCompatibilityLayer.perform(BuildStepCompatibilityLayer.java:80) 14:03:23 at hudson.tasks.BuildStepMonitor$1.perform(BuildStepMonitor.java:20) 14:03:23 at hudson.model.AbstractBuild$AbstractBuildExecution.perform(AbstractBuild.java:818) 14:03:23 at hudson.model.AbstractBuild$AbstractBuildExecution.performAllBuildSteps(AbstractBuild.java:767) 14:03:23 at hudson.model.Build$BuildExecution.post2(Build.java:179) 14:03:23 at hudson.model.AbstractBuild$AbstractBuildExecution.post(AbstractBuild.java:711) 14:03:23 at hudson.model.Run.execute(Run.java:1918) 14:03:23 at hudson.matrix.MatrixRun.run(MatrixRun.java:153) 14:03:23 at hudson.model.ResourceController.execute(ResourceController.java:101) 14:03:23 at hudson.model.Executor.run(Executor.java:442) 14:03:23 Caused by: org.tap4j.parser.ParserException: Duplicated TAP Plan found. 14:03:23 at org.tap4j.parser.Tap13Parser.parseLine(Tap13Parser.java:354) 14:03:23 at org.tap4j.parser.Tap13Parser.parseTapStream(Tap13Parser.java:252) 14:03:23 ... 16 more ``` Cause ===== 09a872ef0b4e1432329aa42fecc61f50e9baa367 introduced multiple plans in to test_ust_constructor Solution ======== Split the script into several smaller test scripts sharing a common import for data and the bulk of execution. Known drawbacks =============== None. Signed-off-by: Kienan Stewart Signed-off-by: Jérémie Galarneau Change-Id: I81649d714afe0e325996b730d5c72cfd5b28d1f8 --- diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index 05cfbc54c..ca3fb2d52 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -91,7 +91,10 @@ TESTS += ust/before-after/test_before_after \ ust/blocking/test_blocking \ ust/multi-lib/test_multi_lib \ ust/rotation-destroy-flush/test_rotation_destroy_flush \ - ust/ust-constructor/test_ust_constructor.py \ + ust/ust-constructor/test_ust_constructor_c_dynamic.py \ + ust/ust-constructor/test_ust_constructor_c_static.py \ + ust/ust-constructor/test_ust_constructor_cpp_dynamic.py \ + ust/ust-constructor/test_ust_constructor_cpp_static.py \ tools/metadata/test_ust \ tools/relayd-grouping/test_ust \ tools/trigger/rate-policy/test_ust_rate_policy diff --git a/tests/regression/ust/ust-constructor/Makefile.am b/tests/regression/ust/ust-constructor/Makefile.am index 4201fdfe8..89267d38d 100644 --- a/tests/regression/ust/ust-constructor/Makefile.am +++ b/tests/regression/ust/ust-constructor/Makefile.am @@ -1,7 +1,19 @@ # SPDX-License-Identifier: GPL-2.0-only -noinst_SCRIPTS = test_ust_constructor.py -EXTRA_DIST = test_ust_constructor.py +noinst_SCRIPTS = __init__.py \ + test_ust_constructor_c_dynamic.py \ + test_ust_constructor_c_static.py \ + test_ust_constructor_cpp_dynamic.py \ + test_ust_constructor_cpp_static.py \ + ust_constructor_common.py + +EXTRA_DIST = __init__.py \ + test_ust_constructor_c_dynamic.py \ + test_ust_constructor_c_static.py \ + test_ust_constructor_cpp_dynamic.py \ + test_ust_constructor_cpp_static.py \ + ust_constructor_common.py + all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ diff --git a/tests/regression/ust/ust-constructor/__init__.py b/tests/regression/ust/ust-constructor/__init__.py new file mode 100644 index 000000000..5acd59f54 --- /dev/null +++ b/tests/regression/ust/ust-constructor/__init__.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCopyrightText: Kienan Stewart +# SPDX-License-Identifier: GPL-2.0-only diff --git a/tests/regression/ust/ust-constructor/test_ust_constructor.py b/tests/regression/ust/ust-constructor/test_ust_constructor.py deleted file mode 100755 index 130d0657d..000000000 --- a/tests/regression/ust/ust-constructor/test_ust_constructor.py +++ /dev/null @@ -1,438 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 Jérémie Galarneau -# Copyright (C) 2023 Mathieu Desnoyers -# -# SPDX-License-Identifier: GPL-2.0-only - -import copy -import pathlib -import sys -import os -import subprocess -from typing import Any, Callable, Type - -""" -Test instrumentation coverage of C/C++ constructors and destructors by LTTng-UST -tracepoints. - -This test successively sets up a session, traces a test application, and then -reads the resulting trace to determine if all the expected events are present. -""" - -# Import in-tree test utils -test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" -sys.path.append(str(test_utils_import_path)) - -import lttngtest -import bt2 - -# Determine if LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP is set. This will -# affect if certain events may or may not be expected when compiling with -# C++. -# @see https://github.com/lttng/lttng-ust/blob/47fa3e4ed7ab43e034dc61fc1480f919f4ee51d0/include/lttng/ust-compiler.h#L51 -# -compound_literal_on_heap = False -process = subprocess.Popen( - [ - os.path.join( - str(test_utils_import_path), - "testapp", - "gen-ust-events-constructor", - "uses_heap", - ) - ] -) -process.wait() -if process.returncode == 0: - compound_literal_on_heap = True - -expected_events_common_cpp = [ - { - "name": "tp:constructor_cplusplus", - "msg": "global - across units before define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_cplusplus", - "msg": "global - same unit before define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_cplusplus", - "msg": "global - same unit after define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_cplusplus", - "msg": "global - across units after define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_cplusplus", - "msg": "global - same unit before provider", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_cplusplus", - "msg": "global - same unit after provider", - "count": 0, - }, - { - "name": "tp:constructor_cplusplus", - "msg": "global - across units after provider", - "count": 0, - }, - {"name": "tp:constructor_cplusplus", "msg": "main() local", "count": 0}, - {"name": "tp:destructor_cplusplus", "msg": "main() local", "count": 0}, - { - "name": "tp:destructor_cplusplus", - "msg": "global - across units after provider", - "count": 0, - }, - { - "name": "tp:destructor_cplusplus", - "msg": "global - same unit after provider", - "count": 0, - }, - { - "name": "tp:destructor_cplusplus", - "msg": "global - same unit before provider", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_cplusplus", - "msg": "global - across units after define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_cplusplus", - "msg": "global - same unit after define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_cplusplus", - "msg": "global - same unit before define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_cplusplus", - "msg": "global - across units before define", - "count": 0, - "may_fail": compound_literal_on_heap, - }, -] - -expected_events_common = [ - { - "name": "tp:constructor_c_across_units_before_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_c_same_unit_before_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_c_same_unit_after_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_c_across_units_after_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_c_same_unit_before_provider", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:constructor_c_same_unit_after_provider", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - {"name": "tp:constructor_c_across_units_after_provider", "msg": None, "count": 0}, - {"name": "tp:main", "msg": None, "count": 0}, - { - "name": "tp:destructor_c_across_units_after_provider", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_c_same_unit_after_provider", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_c_same_unit_before_provider", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_c_across_units_after_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_c_same_unit_after_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_c_same_unit_before_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp:destructor_c_across_units_before_define", - "msg": None, - "count": 0, - "may_fail": compound_literal_on_heap, - }, -] - -expected_events_tp_so_cpp = [ - { - "name": "tp_so:constructor_cplusplus_provider_shared_library", - "msg": "global - shared library define and provider", - "count": 0, - }, - { - "name": "tp_so:constructor_cplusplus_provider_shared_library", - "msg": "main() local - shared library define and provider", - "count": 0, - }, - { - "name": "tp_so:destructor_cplusplus_provider_shared_library", - "msg": "main() local - shared library define and provider", - "count": 0, - }, - { - "name": "tp_so:destructor_cplusplus_provider_shared_library", - "msg": "global - shared library define and provider", - "count": 0, - }, -] - -expected_events_tp_so = [ - {"name": "tp_so_c:constructor_c_provider_shared_library", "msg": None, "count": 0}, - {"name": "tp_so_c:destructor_c_provider_shared_library", "msg": None, "count": 0}, -] - -expected_events_tp_a_cpp = [ - { - "name": "tp_a:constructor_cplusplus_provider_static_archive", - "msg": "global - static archive define and provider", - "count": 0, - "may_fail": compound_literal_on_heap, - }, - { - "name": "tp_a:constructor_cplusplus_provider_static_archive", - "msg": "main() local - static archive define and provider", - "count": 0, - }, - { - "name": "tp_a:destructor_cplusplus_provider_static_archive", - "msg": "main() local - static archive define and provider", - "count": 0, - }, - { - "name": "tp_a:destructor_cplusplus_provider_static_archive", - "msg": "global - static archive define and provider", - "count": 0, - "may_fail": compound_literal_on_heap, - }, -] - -expected_events_tp_a = [ - {"name": "tp_a_c:constructor_c_provider_static_archive", "msg": None, "count": 0}, - {"name": "tp_a_c:destructor_c_provider_static_archive", "msg": None, "count": 0}, -] - - -def capture_trace(tap, test_env, application, description): - # type: (lttngtest.TapGenerator, lttngtest._Environment) -> lttngtest.LocalSessionOutputLocation - tap.diagnostic(description) - - session_output_location = lttngtest.LocalSessionOutputLocation( - test_env.create_temporary_directory("trace") - ) - - client = lttngtest.LTTngClient(test_env, log=tap.diagnostic) - - with tap.case("Create a session") as test_case: - session = client.create_session(output=session_output_location) - tap.diagnostic("Created session `{session_name}`".format(session_name=session.name)) - - with tap.case( - "Add a channel to session `{session_name}`".format(session_name=session.name) - ) as test_case: - channel = session.add_channel(lttngtest.TracingDomain.User) - tap.diagnostic("Created channel `{channel_name}`".format(channel_name=channel.name)) - - # Enable all user space events, the default for a user tracepoint event rule. - channel.add_recording_rule(lttngtest.UserTracepointEventRule("tp*")) - - with tap.case( - "Start session `{session_name}`".format(session_name=session.name) - ) as test_case: - session.start() - - test_app = test_env.launch_test_application(application) - with tap.case( - "Run test app '{}'".format(application, session_name=session.name) - ) as test_case: - test_app.wait_for_exit() - - with tap.case( - "Stop session `{session_name}`".format(session_name=session.name) - ) as test_case: - session.stop() - - with tap.case( - "Destroy session `{session_name}`".format(session_name=session.name) - ) as test_case: - session.destroy() - - return session_output_location - - -def validate_trace(trace_location, tap, expected_events): - # type: (pathlib.Path, lttngtest.TapGenerator) - unknown_event_count = 0 - - for msg in bt2.TraceCollectionMessageIterator(str(trace_location)): - if type(msg) is not bt2._EventMessageConst: - continue - - found = False - for event in expected_events: - if event["name"] == msg.event.name and event["msg"] is None: - found = True - event["count"] = event["count"] + 1 - break - elif ( - event["name"] == msg.event.name - and event["msg"] is not None - and event["msg"] == msg.event["msg"] - ): - found = True - event["count"] = event["count"] + 1 - break - - if found == False: - unknown_event_count = unknown_event_count + 1 - printmsg = None - if "msg" in msg.event: - printmsg = msg.event["msg"] - tap.diagnostic( - 'Unexpected event name="{}" msg="{}" encountered'.format( - msg.event.name, str(printmsg) - ) - ) - - for event in expected_events: - may_fail = "may_fail" in event.keys() and event["may_fail"] - if not may_fail: - tap.test( - event["count"] == 1, - 'Found expected event name="{}" msg="{}"'.format( - event["name"], str(event["msg"]) - ), - ), - else: - tap.skip("Event '{}' may or may not be recorded".format(event["name"])) - - tap.test(unknown_event_count == 0, "Found no unexpected events") - - -success = True -tests = [ - { - "description": "Test user space constructor/destructor instrumentation coverage (C++ w/ static archive)", - "application": "gen-ust-events-constructor/gen-ust-events-constructor-a", - "expected_events": copy.deepcopy( - expected_events_common - + expected_events_common_cpp - + expected_events_tp_a - + expected_events_tp_a_cpp - ), - "skip_if_application_not_present": False, - }, - { - "description": "Test user space constructor/destructor instrumentation coverage (C++ w/ dynamic object", - "application": "gen-ust-events-constructor/gen-ust-events-constructor-so", - "expected_events": copy.deepcopy( - expected_events_common - + expected_events_common_cpp - + expected_events_tp_so - + expected_events_tp_so_cpp - ), - # This application is not be built when `NO_SHARED` is set in the - # configuration options. - "skip_if_application_not_present": True, - }, - { - "description": "Test user space constructor/destructor instrumentation coverage (C w/ static archive)", - "application": "gen-ust-events-constructor/gen-ust-events-c-constructor-a", - "expected_events": copy.deepcopy(expected_events_common + expected_events_tp_a), - "skip_if_application_not_present": False, - }, - { - "description": "Test user space constructor/destructor instrumentation coverage (C w/ dynamic object", - "application": "gen-ust-events-constructor/gen-ust-events-c-constructor-so", - "expected_events": copy.deepcopy( - expected_events_common + expected_events_tp_so - ), - # This application is not be built when `NO_SHARED` is set in the - # configuration options. - "skip_if_application_not_present": True, - }, -] - -success = True -for test in tests: - tap = lttngtest.TapGenerator(7 + len(test["expected_events"])) - with lttngtest.test_environment(with_sessiond=True, log=tap.diagnostic) as test_env: - try: - outputlocation = capture_trace( - tap, test_env, test["application"], test["description"] - ) - except FileNotFoundError as fne: - tap.diagnostic(fne) - if test["skip_if_application_not_present"]: - tap.skip( - "Test application '{}' not found".format(test["application"]), - tap.remaining_test_cases, - ) - break - # Warning: validate_trace mutates test['expected_events'] - validate_trace(outputlocation.path, tap, test["expected_events"]) - success = success and tap.is_successful - - -sys.exit(0 if success else 1) diff --git a/tests/regression/ust/ust-constructor/test_ust_constructor_c_dynamic.py b/tests/regression/ust/ust-constructor/test_ust_constructor_c_dynamic.py new file mode 100755 index 000000000..93ef4c14c --- /dev/null +++ b/tests/regression/ust/ust-constructor/test_ust_constructor_c_dynamic.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCopyrightText: Kienan Stewart +# SPDX-License-Identifier: GPL-2.0-only +# + +""" +Test instrumentation coverage of C constructors and destructors by LTTng-UST +tracepoints with a dynamic object. + +This test successively sets up a session, traces a test application, and then +reads the resulting trace to determine if all the expected events are present. +""" + +import copy +import pathlib +import sys + +# Import in-tree test utils +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest +import ust_constructor_common as ust + +test = { + "description": "Test user space constructor/destructor instrumentation coverage (C w/ dynamic object)", + "application": "gen-ust-events-constructor/gen-ust-events-c-constructor-so", + "expected_events": copy.deepcopy( + ust.expected_events_common + ust.expected_events_tp_so + ), + # This application is not be built when `NO_SHARED` is set in the + # configuration options. + "skip_if_application_not_present": True, +} + +tap = lttngtest.TapGenerator(7 + len(test["expected_events"])) +with lttngtest.test_environment(with_sessiond=True, log=tap.diagnostic) as test_env: + try: + outputlocation = ust.capture_trace( + tap, test_env, test["application"], test["description"] + ) + except FileNotFoundError as fne: + tap.diagnostic(fne) + if test["skip_if_application_not_present"]: + tap.skip( + "Test application '{}' not found".format(test["application"]), + tap.remaining_test_cases, + ) + sys.exit(0) + # Warning: validate_trace mutates test['expected_events'] + ust.validate_trace(outputlocation.path, tap, test["expected_events"]) + + +sys.exit(0 if tap.is_successful else 1) diff --git a/tests/regression/ust/ust-constructor/test_ust_constructor_c_static.py b/tests/regression/ust/ust-constructor/test_ust_constructor_c_static.py new file mode 100755 index 000000000..4fb509a1f --- /dev/null +++ b/tests/regression/ust/ust-constructor/test_ust_constructor_c_static.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCopyrightText: Kienan Stewart +# SPDX-License-Identifier: GPL-2.0-only +# + +""" +Test instrumentation coverage of C constructors and destructors by LTTng-UST +tracepoints with a static archive. + +This test successively sets up a session, traces a test application, and then +reads the resulting trace to determine if all the expected events are present. +""" + +import copy +import pathlib +import sys + +# Import in-tree test utils +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest +import ust_constructor_common as ust + +test = { + "description": "Test user space constructor/destructor instrumentation coverage (C w/ static archive)", + "application": "gen-ust-events-constructor/gen-ust-events-c-constructor-a", + "expected_events": copy.deepcopy( + ust.expected_events_common + ust.expected_events_tp_a + ), + "skip_if_application_not_present": False, +} + +tap = lttngtest.TapGenerator(7 + len(test["expected_events"])) +with lttngtest.test_environment(with_sessiond=True, log=tap.diagnostic) as test_env: + try: + outputlocation = ust.capture_trace( + tap, test_env, test["application"], test["description"] + ) + except FileNotFoundError as fne: + tap.diagnostic(fne) + if test["skip_if_application_not_present"]: + tap.skip( + "Test application '{}' not found".format(test["application"]), + tap.remaining_test_cases, + ) + sys.exit(0) + # Warning: validate_trace mutates test['expected_events'] + ust.validate_trace(outputlocation.path, tap, test["expected_events"]) + + +sys.exit(0 if tap.is_successful else 1) diff --git a/tests/regression/ust/ust-constructor/test_ust_constructor_cpp_dynamic.py b/tests/regression/ust/ust-constructor/test_ust_constructor_cpp_dynamic.py new file mode 100755 index 000000000..cc7988ac1 --- /dev/null +++ b/tests/regression/ust/ust-constructor/test_ust_constructor_cpp_dynamic.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCopyrightText: Kienan Stewart +# SPDX-License-Identifier: GPL-2.0-only +# + +""" +Test instrumentation coverage of C++ constructors and destructors by LTTng-UST +tracepoints with a dynamic object. + +This test successively sets up a session, traces a test application, and then +reads the resulting trace to determine if all the expected events are present. +""" + +import copy +import pathlib +import sys + +# Import in-tree test utils +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest +import ust_constructor_common as ust + +test = { + "description": "Test user space constructor/destructor instrumentation coverage (C++ w/ dynamic object", + "application": "gen-ust-events-constructor/gen-ust-events-constructor-so", + "expected_events": copy.deepcopy( + ust.expected_events_common + + ust.expected_events_common_cpp + + ust.expected_events_tp_so + + ust.expected_events_tp_so_cpp + ), + # This application is not be built when `NO_SHARED` is set in the + # configuration options. + "skip_if_application_not_present": True, +} + +tap = lttngtest.TapGenerator(7 + len(test["expected_events"])) +with lttngtest.test_environment(with_sessiond=True, log=tap.diagnostic) as test_env: + try: + outputlocation = ust.capture_trace( + tap, test_env, test["application"], test["description"] + ) + except FileNotFoundError as fne: + tap.diagnostic(fne) + if test["skip_if_application_not_present"]: + tap.skip( + "Test application '{}' not found".format(test["application"]), + tap.remaining_test_cases, + ) + sys.exit(0) + # Warning: validate_trace mutates test['expected_events'] + ust.validate_trace(outputlocation.path, tap, test["expected_events"]) + + +sys.exit(0 if tap.is_successful else 1) diff --git a/tests/regression/ust/ust-constructor/test_ust_constructor_cpp_static.py b/tests/regression/ust/ust-constructor/test_ust_constructor_cpp_static.py new file mode 100755 index 000000000..db1570df4 --- /dev/null +++ b/tests/regression/ust/ust-constructor/test_ust_constructor_cpp_static.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 Jérémie Galarneau +# Copyright (C) 2023 Mathieu Desnoyers +# +# SPDX-License-Identifier: GPL-2.0-only + + +""" +Test instrumentation coverage of C++ constructors and destructors by LTTng-UST +tracepoints with a static archive. + +This test successively sets up a session, traces a test application, and then +reads the resulting trace to determine if all the expected events are present. +""" + +import copy +import pathlib +import sys + +# Import in-tree test utils +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest +import ust_constructor_common as ust + +test = { + "description": "Test user space constructor/destructor instrumentation coverage (C++ w/ static archive)", + "application": "gen-ust-events-constructor/gen-ust-events-constructor-a", + "expected_events": copy.deepcopy( + ust.expected_events_common + + ust.expected_events_common_cpp + + ust.expected_events_tp_a + + ust.expected_events_tp_a_cpp + ), + "skip_if_application_not_present": False, +} + +tap = lttngtest.TapGenerator(7 + len(test["expected_events"])) +with lttngtest.test_environment(with_sessiond=True, log=tap.diagnostic) as test_env: + try: + outputlocation = ust.capture_trace( + tap, test_env, test["application"], test["description"] + ) + except FileNotFoundError as fne: + tap.diagnostic(fne) + if test["skip_if_application_not_present"]: + tap.skip( + "Test application '{}' not found".format(test["application"]), + tap.remaining_test_cases, + ) + sys.exit(0) + # Warning: validate_trace mutates test['expected_events'] + ust.validate_trace(outputlocation.path, tap, test["expected_events"]) + + +sys.exit(0 if tap.is_successful else 1) diff --git a/tests/regression/ust/ust-constructor/ust_constructor_common.py b/tests/regression/ust/ust-constructor/ust_constructor_common.py new file mode 100644 index 000000000..0ea724810 --- /dev/null +++ b/tests/regression/ust/ust-constructor/ust_constructor_common.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python3 +# +# SPDX-FileCopyrightText: 2024 Kienan Stewart +# SPDX-License-Identifer: GPL-2.0-only +# + +import copy +import pathlib +import sys +import os +import subprocess +from typing import Any, Callable, Type + +# Import in-tree test utils +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest +import bt2 + +# Determine if LTTNG_UST_ALLOCATE_COMPOUND_LITERAL_ON_HEAP is set. This will +# affect if certain events may or may not be expected when compiling with +# C++. +# @see https://github.com/lttng/lttng-ust/blob/47fa3e4ed7ab43e034dc61fc1480f919f4ee51d0/include/lttng/ust-compiler.h#L51 +# +compound_literal_on_heap = False +process = subprocess.Popen( + [ + os.path.join( + str(test_utils_import_path), + "testapp", + "gen-ust-events-constructor", + "uses_heap", + ) + ] +) +process.wait() +if process.returncode == 0: + compound_literal_on_heap = True + +expected_events_common_cpp = [ + { + "name": "tp:constructor_cplusplus", + "msg": "global - across units before define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_cplusplus", + "msg": "global - same unit before define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_cplusplus", + "msg": "global - same unit after define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_cplusplus", + "msg": "global - across units after define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_cplusplus", + "msg": "global - same unit before provider", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_cplusplus", + "msg": "global - same unit after provider", + "count": 0, + }, + { + "name": "tp:constructor_cplusplus", + "msg": "global - across units after provider", + "count": 0, + }, + {"name": "tp:constructor_cplusplus", "msg": "main() local", "count": 0}, + {"name": "tp:destructor_cplusplus", "msg": "main() local", "count": 0}, + { + "name": "tp:destructor_cplusplus", + "msg": "global - across units after provider", + "count": 0, + }, + { + "name": "tp:destructor_cplusplus", + "msg": "global - same unit after provider", + "count": 0, + }, + { + "name": "tp:destructor_cplusplus", + "msg": "global - same unit before provider", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_cplusplus", + "msg": "global - across units after define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_cplusplus", + "msg": "global - same unit after define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_cplusplus", + "msg": "global - same unit before define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_cplusplus", + "msg": "global - across units before define", + "count": 0, + "may_fail": compound_literal_on_heap, + }, +] + +expected_events_common = [ + { + "name": "tp:constructor_c_across_units_before_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_c_same_unit_before_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_c_same_unit_after_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_c_across_units_after_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_c_same_unit_before_provider", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:constructor_c_same_unit_after_provider", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + {"name": "tp:constructor_c_across_units_after_provider", "msg": None, "count": 0}, + {"name": "tp:main", "msg": None, "count": 0}, + { + "name": "tp:destructor_c_across_units_after_provider", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_c_same_unit_after_provider", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_c_same_unit_before_provider", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_c_across_units_after_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_c_same_unit_after_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_c_same_unit_before_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp:destructor_c_across_units_before_define", + "msg": None, + "count": 0, + "may_fail": compound_literal_on_heap, + }, +] + +expected_events_tp_so_cpp = [ + { + "name": "tp_so:constructor_cplusplus_provider_shared_library", + "msg": "global - shared library define and provider", + "count": 0, + }, + { + "name": "tp_so:constructor_cplusplus_provider_shared_library", + "msg": "main() local - shared library define and provider", + "count": 0, + }, + { + "name": "tp_so:destructor_cplusplus_provider_shared_library", + "msg": "main() local - shared library define and provider", + "count": 0, + }, + { + "name": "tp_so:destructor_cplusplus_provider_shared_library", + "msg": "global - shared library define and provider", + "count": 0, + }, +] + +expected_events_tp_so = [ + {"name": "tp_so_c:constructor_c_provider_shared_library", "msg": None, "count": 0}, + {"name": "tp_so_c:destructor_c_provider_shared_library", "msg": None, "count": 0}, +] + +expected_events_tp_a_cpp = [ + { + "name": "tp_a:constructor_cplusplus_provider_static_archive", + "msg": "global - static archive define and provider", + "count": 0, + "may_fail": compound_literal_on_heap, + }, + { + "name": "tp_a:constructor_cplusplus_provider_static_archive", + "msg": "main() local - static archive define and provider", + "count": 0, + }, + { + "name": "tp_a:destructor_cplusplus_provider_static_archive", + "msg": "main() local - static archive define and provider", + "count": 0, + }, + { + "name": "tp_a:destructor_cplusplus_provider_static_archive", + "msg": "global - static archive define and provider", + "count": 0, + "may_fail": compound_literal_on_heap, + }, +] + +expected_events_tp_a = [ + {"name": "tp_a_c:constructor_c_provider_static_archive", "msg": None, "count": 0}, + {"name": "tp_a_c:destructor_c_provider_static_archive", "msg": None, "count": 0}, +] + + +def capture_trace(tap, test_env, application, description): + # type: (lttngtest.TapGenerator, lttngtest._Environment) -> lttngtest.LocalSessionOutputLocation + tap.diagnostic(description) + + session_output_location = lttngtest.LocalSessionOutputLocation( + test_env.create_temporary_directory("trace") + ) + + client = lttngtest.LTTngClient(test_env, log=tap.diagnostic) + + with tap.case("Create a session") as test_case: + session = client.create_session(output=session_output_location) + tap.diagnostic("Created session `{session_name}`".format(session_name=session.name)) + + with tap.case( + "Add a channel to session `{session_name}`".format(session_name=session.name) + ) as test_case: + channel = session.add_channel(lttngtest.TracingDomain.User) + tap.diagnostic("Created channel `{channel_name}`".format(channel_name=channel.name)) + + # Enable all user space events, the default for a user tracepoint event rule. + channel.add_recording_rule(lttngtest.UserTracepointEventRule("tp*")) + + with tap.case( + "Start session `{session_name}`".format(session_name=session.name) + ) as test_case: + session.start() + + test_app = test_env.launch_test_application(application) + with tap.case( + "Run test app '{}'".format(application, session_name=session.name) + ) as test_case: + test_app.wait_for_exit() + + with tap.case( + "Stop session `{session_name}`".format(session_name=session.name) + ) as test_case: + session.stop() + + with tap.case( + "Destroy session `{session_name}`".format(session_name=session.name) + ) as test_case: + session.destroy() + + return session_output_location + + +def validate_trace(trace_location, tap, expected_events): + # type: (pathlib.Path, lttngtest.TapGenerator) + unknown_event_count = 0 + + for msg in bt2.TraceCollectionMessageIterator(str(trace_location)): + if type(msg) is not bt2._EventMessageConst: + continue + + found = False + for event in expected_events: + if event["name"] == msg.event.name and event["msg"] is None: + found = True + event["count"] = event["count"] + 1 + break + elif ( + event["name"] == msg.event.name + and event["msg"] is not None + and event["msg"] == msg.event["msg"] + ): + found = True + event["count"] = event["count"] + 1 + break + + if found == False: + unknown_event_count = unknown_event_count + 1 + printmsg = None + if "msg" in msg.event: + printmsg = msg.event["msg"] + tap.diagnostic( + 'Unexpected event name="{}" msg="{}" encountered'.format( + msg.event.name, str(printmsg) + ) + ) + + for event in expected_events: + may_fail = "may_fail" in event.keys() and event["may_fail"] + if not may_fail: + tap.test( + event["count"] == 1, + 'Found expected event name="{}" msg="{}"'.format( + event["name"], str(event["msg"]) + ), + ) + else: + tap.skip("Event '{}' may or may not be recorded".format(event["name"])) + + tap.test(unknown_event_count == 0, "Found no unexpected events")