tests: Split test_ust_constructor into several tests
authorKienan Stewart <kstewart@efficios.com>
Thu, 7 Mar 2024 20:20:17 +0000 (15:20 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 8 Mar 2024 16:34:33 +0000 (11:34 -0500)
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 <kstewart@efficios.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: I81649d714afe0e325996b730d5c72cfd5b28d1f8

tests/regression/Makefile.am
tests/regression/ust/ust-constructor/Makefile.am
tests/regression/ust/ust-constructor/__init__.py [new file with mode: 0644]
tests/regression/ust/ust-constructor/test_ust_constructor.py [deleted file]
tests/regression/ust/ust-constructor/test_ust_constructor_c_dynamic.py [new file with mode: 0755]
tests/regression/ust/ust-constructor/test_ust_constructor_c_static.py [new file with mode: 0755]
tests/regression/ust/ust-constructor/test_ust_constructor_cpp_dynamic.py [new file with mode: 0755]
tests/regression/ust/ust-constructor/test_ust_constructor_cpp_static.py [new file with mode: 0755]
tests/regression/ust/ust-constructor/ust_constructor_common.py [new file with mode: 0644]

index 05cfbc54c03e80fab2fd28449cb59512c5507b98..ca3fb2d52d281968f9ffb9daeca2e47035c4bb53 100644 (file)
@@ -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
index 4201fdfe8540d2496e41ecaa5f80d9a143974962..89267d38d490cc0b553f8e09c639470557e93162 100644 (file)
@@ -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 (file)
index 0000000..5acd59f
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env python3
+#
+# SPDX-FileCopyrightText: Kienan Stewart <kstewart@efficios.com>
+# 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 (executable)
index 130d065..0000000
+++ /dev/null
@@ -1,438 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
-# Copyright (C) 2023 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
-#
-# 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 (executable)
index 0000000..93ef4c1
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+#
+# SPDX-FileCopyrightText: Kienan Stewart <kstewart@efficios.com>
+# 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 (executable)
index 0000000..4fb509a
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# SPDX-FileCopyrightText: Kienan Stewart <kstewart@efficios.com>
+# 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 (executable)
index 0000000..cc7988a
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# SPDX-FileCopyrightText: Kienan Stewart <kstewart@efficios.com>
+# 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 (executable)
index 0000000..db1570d
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+# Copyright (C) 2023 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+#
+# 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 (file)
index 0000000..0ea7248
--- /dev/null
@@ -0,0 +1,361 @@
+#!/usr/bin/env python3
+#
+# SPDX-FileCopyrightText: 2024 Kienan Stewart <kstewart@efficios.com>
+# 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")
This page took 0.037216 seconds and 4 git commands to generate.