From 26efa90d658df03ef1cc937c57641b3dcadd6095 Mon Sep 17 00:00:00 2001 From: Christophe Bedard Date: Mon, 10 Jul 2023 11:14:47 -0700 Subject: [PATCH] Add lttngpy Signed-off-by: Christophe Bedard --- .github/workflows/test.yml | 1 + LTTng_QUALITY_DECLARATION.md | 9 +- README.md | 8 + lttngpy/.gitignore | 2 + lttngpy/CMakeLists.txt | 129 ++++++++ lttngpy/lttngpy/__init__.py | 20 ++ lttngpy/lttngpy/impl.py | 18 ++ lttngpy/package.xml | 31 ++ lttngpy/src/lttngpy/_lttngpy_pybind11.cpp | 342 ++++++++++++++++++++++ lttngpy/src/lttngpy/channel.cpp | 87 ++++++ lttngpy/src/lttngpy/channel.hpp | 57 ++++ lttngpy/src/lttngpy/config.hpp.in | 21 ++ lttngpy/src/lttngpy/context_app.cpp | 58 ++++ lttngpy/src/lttngpy/context_app.hpp | 49 ++++ lttngpy/src/lttngpy/context_lttng.cpp | 90 ++++++ lttngpy/src/lttngpy/context_lttng.hpp | 36 +++ lttngpy/src/lttngpy/context_perf.cpp | 296 +++++++++++++++++++ lttngpy/src/lttngpy/context_perf.hpp | 143 +++++++++ lttngpy/src/lttngpy/event.cpp | 211 +++++++++++++ lttngpy/src/lttngpy/event.hpp | 69 +++++ lttngpy/src/lttngpy/lttng.cpp | 27 ++ lttngpy/src/lttngpy/lttng.hpp | 32 ++ lttngpy/src/lttngpy/session.cpp | 67 +++++ lttngpy/src/lttngpy/session.hpp | 43 +++ lttngpy/src/lttngpy/status.cpp | 30 ++ lttngpy/src/lttngpy/status.hpp | 35 +++ lttngpy/src/lttngpy/utils.hpp | 47 +++ lttngpy/test/test_constants.py | 29 ++ lttngpy/test/test_context_app.cpp | 38 +++ lttngpy/test/test_context_lttng.cpp | 31 ++ lttngpy/test/test_context_perf.cpp | 70 +++++ lttngpy/test/test_session.py | 68 +++++ 32 files changed, 2192 insertions(+), 2 deletions(-) create mode 100644 lttngpy/.gitignore create mode 100644 lttngpy/CMakeLists.txt create mode 100644 lttngpy/lttngpy/__init__.py create mode 100644 lttngpy/lttngpy/impl.py create mode 100644 lttngpy/package.xml create mode 100644 lttngpy/src/lttngpy/_lttngpy_pybind11.cpp create mode 100644 lttngpy/src/lttngpy/channel.cpp create mode 100644 lttngpy/src/lttngpy/channel.hpp create mode 100644 lttngpy/src/lttngpy/config.hpp.in create mode 100644 lttngpy/src/lttngpy/context_app.cpp create mode 100644 lttngpy/src/lttngpy/context_app.hpp create mode 100644 lttngpy/src/lttngpy/context_lttng.cpp create mode 100644 lttngpy/src/lttngpy/context_lttng.hpp create mode 100644 lttngpy/src/lttngpy/context_perf.cpp create mode 100644 lttngpy/src/lttngpy/context_perf.hpp create mode 100644 lttngpy/src/lttngpy/event.cpp create mode 100644 lttngpy/src/lttngpy/event.hpp create mode 100644 lttngpy/src/lttngpy/lttng.cpp create mode 100644 lttngpy/src/lttngpy/lttng.hpp create mode 100644 lttngpy/src/lttngpy/session.cpp create mode 100644 lttngpy/src/lttngpy/session.hpp create mode 100644 lttngpy/src/lttngpy/status.cpp create mode 100644 lttngpy/src/lttngpy/status.hpp create mode 100644 lttngpy/src/lttngpy/utils.hpp create mode 100644 lttngpy/test/test_constants.py create mode 100644 lttngpy/test/test_context_app.cpp create mode 100644 lttngpy/test/test_context_lttng.cpp create mode 100644 lttngpy/test/test_context_perf.cpp create mode 100644 lttngpy/test/test_session.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39f202ff..3ec42a43 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,6 +49,7 @@ jobs: - uses: ros-tooling/action-ros-ci@master with: package-name: > + lttngpy ros2trace test_ros2trace test_tracetools diff --git a/LTTng_QUALITY_DECLARATION.md b/LTTng_QUALITY_DECLARATION.md index 5fc1225b..bc3a7281 100644 --- a/LTTng_QUALITY_DECLARATION.md +++ b/LTTng_QUALITY_DECLARATION.md @@ -88,16 +88,21 @@ Features are listed and well documented on the [LTTng website](https://lttng.org LTTng packages have embedded API documentation. It can be viewed on their man pages: -* [`lttng-ust`](https://lttng.org/man/3/lttng-ust/v2.11/) +* [`lttng-ust`](https://lttng.org/man/3/lttng-ust/v2.13/) ### License [3.iii] All repositories have a `LICENSE` file. All relevant files have a license identifier. -* `lttng-tools` is licensed under LGPLv2.1 and GPLv2, see [`LICENSE` file](https://github.com/lttng/lttng-tools/blob/master/LICENSE) * `lttng-ust` is licensed under LGPLv2.1, the MIT license and GPLv2, see [`LICENSE` file](https://github.com/lttng/lttng-ust/blob/master/LICENSE) + * `liblttng-ust` (build dependency) is LGPLv2.1 and MIT + * The rest (runtime tools, not dependencies) is GPLv2 +* `lttng-tools` is licensed under LGPLv2.1 and GPLv2, see [`LICENSE` file](https://github.com/lttng/lttng-tools/blob/master/LICENSE) + * `liblttng-ctl` (build dependency) is LGPLv2.1 + * The rest (runtime tools, not dependencies) is GPLv2 * `lttng-modules` is licensed under LGPLv2.1, GPLv2 and the MIT license, see [`LICENSE` file](https://github.com/lttng/lttng-modules/blob/master/LICENSE) + * Not a dependency ### Copyright Statement [3.iv] diff --git a/README.md b/README.md index 9079093e..cf56ce57 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,10 @@ The LTTng kernel tracer has a similar implementation, but is separate from the u ## Packages +### lttngpy + +Package containing `liblttng-ctl` Python bindings. + ### ros2trace Package containing a `ros2cli` extension to enable tracing. @@ -246,6 +250,10 @@ Package containing tools for tracing-related tests. Package containing tools to enable tracing. +### test_ros2trace + +Package containing system tests for `ros2trace`. + ### test_tracetools Package containing unit and system tests for `tracetools`. diff --git a/lttngpy/.gitignore b/lttngpy/.gitignore new file mode 100644 index 00000000..2f836aac --- /dev/null +++ b/lttngpy/.gitignore @@ -0,0 +1,2 @@ +*~ +*.pyc diff --git a/lttngpy/CMakeLists.txt b/lttngpy/CMakeLists.txt new file mode 100644 index 00000000..222984bd --- /dev/null +++ b/lttngpy/CMakeLists.txt @@ -0,0 +1,129 @@ +cmake_minimum_required(VERSION 3.12) + +project(lttngpy) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() +# Default to C11 +if(NOT CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 11) +endif() +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake REQUIRED) + +if(WIN32 OR APPLE OR ANDROID) + set(DISABLED_DEFAULT ON) +else() + set(DISABLED_DEFAULT OFF) +endif() +option( + LTTNGPY_DISABLED + "Explicitly disable support, don't link against liblttng-ctl" + ${DISABLED_DEFAULT}) + +# Find python before pybind11 +find_package(Python3 REQUIRED COMPONENTS Interpreter Development) + +find_package(pybind11_vendor REQUIRED) +find_package(pybind11 REQUIRED) + +if(NOT LTTNGPY_DISABLED) + find_package(PkgConfig REQUIRED) + pkg_check_modules(LTTNG_CTL REQUIRED lttng-ctl) + set(LTTNG_CTL_VERSION ${LTTNG_CTL_VERSION}) +else() + set(LTTNG_CTL_VERSION "") +endif() + +# Store configuration variable for buildtime use +# LTTNGPY_DISABLED +# LTTNG_CTL_VERSION +configure_file(src/lttngpy/config.hpp.in src/lttngpy/config.hpp) + +ament_python_install_package(${PROJECT_NAME}) + +set(SOURCES + src/lttngpy/_lttngpy_pybind11.cpp + src/lttngpy/status.cpp +) +if(NOT LTTNGPY_DISABLED) + list(APPEND SOURCES + src/lttngpy/channel.cpp + src/lttngpy/context_app.cpp + src/lttngpy/context_lttng.cpp + src/lttngpy/context_perf.cpp + src/lttngpy/event.cpp + src/lttngpy/lttng.cpp + src/lttngpy/session.cpp + ) +endif() + +pybind11_add_module(_lttngpy_pybind11 SHARED ${SOURCES}) + +if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + target_link_libraries(_lttngpy_pybind11 PRIVATE atomic) +endif() + +target_include_directories(_lttngpy_pybind11 PRIVATE + "$" + "$" +) +if(NOT LTTNGPY_DISABLED) + target_link_libraries(_lttngpy_pybind11 PRIVATE ${LTTNG_CTL_LIBRARIES}) +endif() + +# Set the build location and install location for a CPython extension +install(TARGETS _lttngpy_pybind11 + DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}" +) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + + if(NOT LTTNGPY_DISABLED) + find_package(ament_cmake_gtest REQUIRED) + find_package(ament_cmake_pytest REQUIRED) + + # Using source files, because I can't seem to be able to link against _lttngpy_pybind11 + ament_add_gtest(test_context_app test/test_context_app.cpp src/lttngpy/context_app.cpp) + if(TARGET test_context_app) + target_include_directories(test_context_app PRIVATE src/) + endif() + + ament_add_gtest(test_context_lttng test/test_context_lttng.cpp src/lttngpy/context_lttng.cpp) + if(TARGET test_context_lttng) + target_link_libraries(test_context_lttng ${LTTNG_CTL_LIBRARIES}) + target_include_directories(test_context_lttng PRIVATE src/) + endif() + + ament_add_gtest(test_context_perf test/test_context_perf.cpp src/lttngpy/context_perf.cpp) + if(TARGET test_context_perf) + target_link_libraries(test_context_perf ${LTTNG_CTL_LIBRARIES}) + target_include_directories(test_context_perf PRIVATE src/) + endif() + + set(_lttngpy_pytest_tests + test/test_constants.py + test/test_session.py + ) + + foreach(_test_path ${_lttngpy_pytest_tests}) + get_filename_component(_test_name ${_test_path} NAME_WE) + ament_add_pytest_test(${_test_name} ${_test_path} + APPEND_ENV AMENT_PREFIX_PATH=${ament_index_build_path} + PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR} + TIMEOUT 120 + WERROR ON + ) + endforeach() + endif() +endif() + +ament_package() diff --git a/lttngpy/lttngpy/__init__.py b/lttngpy/lttngpy/__init__.py new file mode 100644 index 00000000..fdee7d25 --- /dev/null +++ b/lttngpy/lttngpy/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2023 Apex.AI, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .impl import impl + + +__all__ = [ + 'impl', +] diff --git a/lttngpy/lttngpy/impl.py b/lttngpy/lttngpy/impl.py new file mode 100644 index 00000000..1f381376 --- /dev/null +++ b/lttngpy/lttngpy/impl.py @@ -0,0 +1,18 @@ +# Copyright 2023 Apex.AI, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from rpyutils import import_c_library + + +impl = import_c_library('._lttngpy_pybind11', 'lttngpy') diff --git a/lttngpy/package.xml b/lttngpy/package.xml new file mode 100644 index 00000000..6073f3df --- /dev/null +++ b/lttngpy/package.xml @@ -0,0 +1,31 @@ + + + + lttngpy + 7.1.0 + liblttng-ctl Python bindings + Christophe Bedard + Apache License 2.0 + https://index.ros.org/p/lttngpy/ + https://github.com/ros2/ros2_tracing + https://github.com/ros2/ros2_tracing/issues + Christophe Bedard + + ament_cmake + python_cmake_module + + liblttng-ctl-dev + + pybind11_vendor + + rpyutils + + ament_cmake_gtest + ament_cmake_pytest + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/lttngpy/src/lttngpy/_lttngpy_pybind11.cpp b/lttngpy/src/lttngpy/_lttngpy_pybind11.cpp new file mode 100644 index 00000000..fefc1d87 --- /dev/null +++ b/lttngpy/src/lttngpy/_lttngpy_pybind11.cpp @@ -0,0 +1,342 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lttngpy/config.hpp" + +#ifndef LTTNGPY_DISABLED +# include +#endif // LTTNGPY_DISABLED +#include +#include + +#ifndef LTTNGPY_DISABLED +# include "lttngpy/channel.hpp" +# include "lttngpy/event.hpp" +# include "lttngpy/lttng.hpp" +# include "lttngpy/session.hpp" +#endif // LTTNGPY_DISABLED +#include "lttngpy/status.hpp" + +namespace py = pybind11; + +PYBIND11_MODULE(_lttngpy_pybind11, m) { + /** + * Some lttng-ctl functions and constants are directly exposed, others are wrapped (e.g., to make + * the interface more Pythonic/use Python types) and the wrapper functions are exposed. The + * exposed lttng-ctl functions and constants usually start with lttng_* or LTTNG_*, respectively. + */ + m.doc() = "LTTng trace control bindings"; + + // Status + m.def( + "is_available", + <tngpy::is_available, + "Check if lttng-ctl is available. If False, then no other functions are available."); + +#ifndef LTTNGPY_DISABLED + // General + m.attr("LTTNG_CTL_VERSION") = LTTNG_CTL_VERSION; + + // Session daemon + m.def( + "lttng_session_daemon_alive", + <tng_session_daemon_alive, + "Check if a session daemon is alive."); + m.def( + "is_lttng_session_daemon_alive", + <tngpy::is_lttng_session_daemon_alive, + "Check if a session daemon is alive."); + + // Session creation/destruction + m.def( + "lttng_create_session", + <tng_create_session, + "Create session.", + py::kw_only(), + py::arg("session_name"), + py::arg("url")); + m.def( + "lttng_destroy_session", + <tng_destroy_session, + "Destroy session.", + py::kw_only(), + py::arg("session_name")); + m.def( + "destroy_all_sessions", + <tngpy::destroy_all_sessions, + "Destroy all sessions."); + + // Session control + m.def( + "lttng_start_tracing", + <tng_start_tracing, + "Start tracing.", + py::kw_only(), + py::arg("session_name")); + m.def( + "lttng_stop_tracing", + <tng_stop_tracing, + "Stop tracing.", + py::kw_only(), + py::arg("session_name")); + + // Session info + m.def( + "get_session_names", + <tngpy::get_session_names, + "Get the currently-existing session names."); + + // Domain + py::enum_(m, "lttng_domain_type") + .value("LTTNG_DOMAIN_NONE", LTTNG_DOMAIN_NONE) + .value("LTTNG_DOMAIN_KERNEL", LTTNG_DOMAIN_KERNEL) + .value("LTTNG_DOMAIN_UST", LTTNG_DOMAIN_UST) + .value("LTTNG_DOMAIN_JUL", LTTNG_DOMAIN_JUL) + .value("LTTNG_DOMAIN_LOG4J", LTTNG_DOMAIN_LOG4J) + .value("LTTNG_DOMAIN_PYTHON", LTTNG_DOMAIN_PYTHON) + .export_values(); + py::enum_(m, "lttng_buffer_type") + .value("LTTNG_BUFFER_PER_PID", LTTNG_BUFFER_PER_PID) + .value("LTTNG_BUFFER_PER_UID", LTTNG_BUFFER_PER_UID) + .value("LTTNG_BUFFER_GLOBAL", LTTNG_BUFFER_GLOBAL) + .export_values(); + + // Channel + m.def( + "enable_channel", + <tngpy::enable_channel, + "Enable channel.", + py::kw_only(), + py::arg("session_name"), + py::arg("domain_type"), + py::arg("buffer_type"), + py::arg("channel_name"), + py::arg("overwrite"), + py::arg("subbuf_size"), + py::arg("num_subbuf"), + py::arg("switch_timer_interval"), + py::arg("read_timer_interval"), + py::arg("output")); + + // Event + py::enum_(m, "lttng_event_output") + .value("LTTNG_EVENT_SPLICE", LTTNG_EVENT_SPLICE) + .value("LTTNG_EVENT_MMAP", LTTNG_EVENT_MMAP) + .export_values(); + m.def( + "enable_events", + <tngpy::enable_events, + "Enable event.", + py::kw_only(), + py::arg("session_name"), + py::arg("domain_type"), + py::arg("channel_name"), + py::arg("events")); + m.def( + "get_tracepoints", + <tngpy::get_tracepoints, + "Get tracepoints.", + py::kw_only(), + py::arg("domain_type")); + m.def( + "add_contexts", + <tngpy::add_contexts, + "Add contexts.", + py::kw_only(), + py::arg("session_name"), + py::arg("domain_type"), + py::arg("channel_name"), + py::arg("context_fields")); + + // Error + m.def( + "lttng_strerror", + <tng_strerror, + "Get string representation of (negative) error code.", + py::arg("code")); + py::enum_(m, "lttng_error_code") + .value("LTTNG_OK", LTTNG_OK) + .value("LTTNG_ERR_UNK", LTTNG_ERR_UNK) + .value("LTTNG_ERR_UND", LTTNG_ERR_UND) + .value("LTTNG_ERR_SESSION_STARTED", LTTNG_ERR_SESSION_STARTED) + .value("LTTNG_ERR_UNKNOWN_DOMAIN", LTTNG_ERR_UNKNOWN_DOMAIN) + .value("LTTNG_ERR_NOT_SUPPORTED", LTTNG_ERR_NOT_SUPPORTED) + .value("LTTNG_ERR_NO_SESSION", LTTNG_ERR_NO_SESSION) + .value("LTTNG_ERR_CREATE_DIR_FAIL", LTTNG_ERR_CREATE_DIR_FAIL) + .value("LTTNG_ERR_SESSION_FAIL", LTTNG_ERR_SESSION_FAIL) + .value("LTTNG_ERR_NO_SESSIOND", LTTNG_ERR_NO_SESSIOND) + .value("LTTNG_ERR_SET_URL", LTTNG_ERR_SET_URL) + .value("LTTNG_ERR_URL_EXIST", LTTNG_ERR_URL_EXIST) + .value("LTTNG_ERR_BUFFER_NOT_SUPPORTED", LTTNG_ERR_BUFFER_NOT_SUPPORTED) + .value("LTTNG_ERR_SESS_NOT_FOUND", LTTNG_ERR_SESS_NOT_FOUND) + .value("LTTNG_ERR_BUFFER_TYPE_MISMATCH", LTTNG_ERR_BUFFER_TYPE_MISMATCH) + .value("LTTNG_ERR_FATAL", LTTNG_ERR_FATAL) + .value("LTTNG_ERR_NOMEM", LTTNG_ERR_NOMEM) + .value("LTTNG_ERR_SELECT_SESS", LTTNG_ERR_SELECT_SESS) + .value("LTTNG_ERR_EXIST_SESS", LTTNG_ERR_EXIST_SESS) + .value("LTTNG_ERR_NO_EVENT", LTTNG_ERR_NO_EVENT) + .value("LTTNG_ERR_CONNECT_FAIL", LTTNG_ERR_CONNECT_FAIL) + .value("LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST", LTTNG_ERR_SNAPSHOT_OUTPUT_EXIST) + .value("LTTNG_ERR_EPERM", LTTNG_ERR_EPERM) + .value("LTTNG_ERR_KERN_NA", LTTNG_ERR_KERN_NA) + .value("LTTNG_ERR_KERN_VERSION", LTTNG_ERR_KERN_VERSION) + .value("LTTNG_ERR_KERN_EVENT_EXIST", LTTNG_ERR_KERN_EVENT_EXIST) + .value("LTTNG_ERR_KERN_SESS_FAIL", LTTNG_ERR_KERN_SESS_FAIL) + .value("LTTNG_ERR_KERN_CHAN_EXIST", LTTNG_ERR_KERN_CHAN_EXIST) + .value("LTTNG_ERR_KERN_CHAN_FAIL", LTTNG_ERR_KERN_CHAN_FAIL) + .value("LTTNG_ERR_KERN_CHAN_NOT_FOUND", LTTNG_ERR_KERN_CHAN_NOT_FOUND) + .value("LTTNG_ERR_KERN_CHAN_DISABLE_FAIL", LTTNG_ERR_KERN_CHAN_DISABLE_FAIL) + .value("LTTNG_ERR_KERN_CHAN_ENABLE_FAIL", LTTNG_ERR_KERN_CHAN_ENABLE_FAIL) + .value("LTTNG_ERR_KERN_CONTEXT_FAIL", LTTNG_ERR_KERN_CONTEXT_FAIL) + .value("LTTNG_ERR_KERN_ENABLE_FAIL", LTTNG_ERR_KERN_ENABLE_FAIL) + .value("LTTNG_ERR_KERN_DISABLE_FAIL", LTTNG_ERR_KERN_DISABLE_FAIL) + .value("LTTNG_ERR_KERN_META_FAIL", LTTNG_ERR_KERN_META_FAIL) + .value("LTTNG_ERR_KERN_START_FAIL", LTTNG_ERR_KERN_START_FAIL) + .value("LTTNG_ERR_KERN_STOP_FAIL", LTTNG_ERR_KERN_STOP_FAIL) + .value("LTTNG_ERR_KERN_CONSUMER_FAIL", LTTNG_ERR_KERN_CONSUMER_FAIL) + .value("LTTNG_ERR_KERN_STREAM_FAIL", LTTNG_ERR_KERN_STREAM_FAIL) + .value("LTTNG_ERR_START_SESSION_ONCE", LTTNG_ERR_START_SESSION_ONCE) + .value("LTTNG_ERR_SNAPSHOT_FAIL", LTTNG_ERR_SNAPSHOT_FAIL) + .value("LTTNG_ERR_NO_STREAM", LTTNG_ERR_NO_STREAM) + .value("LTTNG_ERR_KERN_LIST_FAIL", LTTNG_ERR_KERN_LIST_FAIL) + .value("LTTNG_ERR_UST_CALIBRATE_FAIL", LTTNG_ERR_UST_CALIBRATE_FAIL) + .value("LTTNG_ERR_UST_EVENT_ENABLED", LTTNG_ERR_UST_EVENT_ENABLED) + .value("LTTNG_ERR_UST_SESS_FAIL", LTTNG_ERR_UST_SESS_FAIL) + .value("LTTNG_ERR_UST_CHAN_EXIST", LTTNG_ERR_UST_CHAN_EXIST) + .value("LTTNG_ERR_UST_CHAN_FAIL", LTTNG_ERR_UST_CHAN_FAIL) + .value("LTTNG_ERR_UST_CHAN_NOT_FOUND", LTTNG_ERR_UST_CHAN_NOT_FOUND) + .value("LTTNG_ERR_UST_CHAN_DISABLE_FAIL", LTTNG_ERR_UST_CHAN_DISABLE_FAIL) + .value("LTTNG_ERR_UST_CHAN_ENABLE_FAIL", LTTNG_ERR_UST_CHAN_ENABLE_FAIL) + .value("LTTNG_ERR_CHAN_EXIST", LTTNG_ERR_CHAN_EXIST) + .value("LTTNG_ERR_UST_ENABLE_FAIL", LTTNG_ERR_UST_ENABLE_FAIL) + .value("LTTNG_ERR_UST_DISABLE_FAIL", LTTNG_ERR_UST_DISABLE_FAIL) + .value("LTTNG_ERR_UST_META_FAIL", LTTNG_ERR_UST_META_FAIL) + .value("LTTNG_ERR_UST_START_FAIL", LTTNG_ERR_UST_START_FAIL) + .value("LTTNG_ERR_UST_STOP_FAIL", LTTNG_ERR_UST_STOP_FAIL) + .value("LTTNG_ERR_UST_CONSUMER64_FAIL", LTTNG_ERR_UST_CONSUMER64_FAIL) + .value("LTTNG_ERR_UST_CONSUMER32_FAIL", LTTNG_ERR_UST_CONSUMER32_FAIL) + .value("LTTNG_ERR_UST_STREAM_FAIL", LTTNG_ERR_UST_STREAM_FAIL) + .value("LTTNG_ERR_SNAPSHOT_NODATA", LTTNG_ERR_SNAPSHOT_NODATA) + .value("LTTNG_ERR_NO_CHANNEL", LTTNG_ERR_NO_CHANNEL) + .value("LTTNG_ERR_SESSION_INVALID_CHAR", LTTNG_ERR_SESSION_INVALID_CHAR) + .value("LTTNG_ERR_UST_LIST_FAIL", LTTNG_ERR_UST_LIST_FAIL) + .value("LTTNG_ERR_UST_EVENT_EXIST", LTTNG_ERR_UST_EVENT_EXIST) + .value("LTTNG_ERR_UST_EVENT_NOT_FOUND", LTTNG_ERR_UST_EVENT_NOT_FOUND) + .value("LTTNG_ERR_UST_CONTEXT_EXIST", LTTNG_ERR_UST_CONTEXT_EXIST) + .value("LTTNG_ERR_UST_CONTEXT_INVAL", LTTNG_ERR_UST_CONTEXT_INVAL) + .value("LTTNG_ERR_NEED_ROOT_SESSIOND", LTTNG_ERR_NEED_ROOT_SESSIOND) + .value("LTTNG_ERR_TRACE_ALREADY_STARTED", LTTNG_ERR_TRACE_ALREADY_STARTED) + .value("LTTNG_ERR_TRACE_ALREADY_STOPPED", LTTNG_ERR_TRACE_ALREADY_STOPPED) + .value("LTTNG_ERR_KERN_EVENT_ENOSYS", LTTNG_ERR_KERN_EVENT_ENOSYS) + .value("LTTNG_ERR_NEED_CHANNEL_NAME", LTTNG_ERR_NEED_CHANNEL_NAME) + .value("LTTNG_ERR_NO_UST", LTTNG_ERR_NO_UST) + .value("LTTNG_ERR_SAVE_FILE_EXIST", LTTNG_ERR_SAVE_FILE_EXIST) + .value("LTTNG_ERR_SAVE_IO_FAIL", LTTNG_ERR_SAVE_IO_FAIL) + .value("LTTNG_ERR_LOAD_INVALID_CONFIG", LTTNG_ERR_LOAD_INVALID_CONFIG) + .value("LTTNG_ERR_LOAD_IO_FAIL", LTTNG_ERR_LOAD_IO_FAIL) + .value("LTTNG_ERR_LOAD_SESSION_NOENT", LTTNG_ERR_LOAD_SESSION_NOENT) + .value("LTTNG_ERR_MAX_SIZE_INVALID", LTTNG_ERR_MAX_SIZE_INVALID) + .value("LTTNG_ERR_MI_OUTPUT_TYPE", LTTNG_ERR_MI_OUTPUT_TYPE) + .value("LTTNG_ERR_MI_IO_FAIL", LTTNG_ERR_MI_IO_FAIL) + .value("LTTNG_ERR_MI_NOT_IMPLEMENTED", LTTNG_ERR_MI_NOT_IMPLEMENTED) + .value("LTTNG_ERR_INVALID", LTTNG_ERR_INVALID) + .value("LTTNG_ERR_NO_USTCONSUMERD", LTTNG_ERR_NO_USTCONSUMERD) + .value("LTTNG_ERR_NO_KERNCONSUMERD", LTTNG_ERR_NO_KERNCONSUMERD) + .value("LTTNG_ERR_EVENT_EXIST_LOGLEVEL", LTTNG_ERR_EVENT_EXIST_LOGLEVEL) + .value("LTTNG_ERR_URL_DATA_MISS", LTTNG_ERR_URL_DATA_MISS) + .value("LTTNG_ERR_URL_CTRL_MISS", LTTNG_ERR_URL_CTRL_MISS) + .value("LTTNG_ERR_ENABLE_CONSUMER_FAIL", LTTNG_ERR_ENABLE_CONSUMER_FAIL) + .value("LTTNG_ERR_RELAYD_CONNECT_FAIL", LTTNG_ERR_RELAYD_CONNECT_FAIL) + .value("LTTNG_ERR_RELAYD_VERSION_FAIL", LTTNG_ERR_RELAYD_VERSION_FAIL) + .value("LTTNG_ERR_FILTER_INVAL", LTTNG_ERR_FILTER_INVAL) + .value("LTTNG_ERR_FILTER_NOMEM", LTTNG_ERR_FILTER_NOMEM) + .value("LTTNG_ERR_FILTER_EXIST", LTTNG_ERR_FILTER_EXIST) + .value("LTTNG_ERR_NO_CONSUMER", LTTNG_ERR_NO_CONSUMER) + .value("LTTNG_ERR_EXCLUSION_INVAL", LTTNG_ERR_EXCLUSION_INVAL) + .value("LTTNG_ERR_EXCLUSION_NOMEM", LTTNG_ERR_EXCLUSION_NOMEM) + .value("LTTNG_ERR_INVALID_EVENT_NAME", LTTNG_ERR_INVALID_EVENT_NAME) + .value("LTTNG_ERR_INVALID_CHANNEL_NAME", LTTNG_ERR_INVALID_CHANNEL_NAME) + .value("LTTNG_ERR_PROCESS_ATTR_EXISTS", LTTNG_ERR_PROCESS_ATTR_EXISTS) + .value("LTTNG_ERR_PROCESS_ATTR_MISSING", LTTNG_ERR_PROCESS_ATTR_MISSING) + .value("LTTNG_ERR_INVALID_CHANNEL_DOMAIN", LTTNG_ERR_INVALID_CHANNEL_DOMAIN) + .value("LTTNG_ERR_OVERFLOW", LTTNG_ERR_OVERFLOW) + .value("LTTNG_ERR_SESSION_NOT_STARTED", LTTNG_ERR_SESSION_NOT_STARTED) + .value("LTTNG_ERR_LIVE_SESSION", LTTNG_ERR_LIVE_SESSION) + .value("LTTNG_ERR_PER_PID_SESSION", LTTNG_ERR_PER_PID_SESSION) + .value("LTTNG_ERR_KERN_CONTEXT_UNAVAILABLE", LTTNG_ERR_KERN_CONTEXT_UNAVAILABLE) + .value("LTTNG_ERR_REGEN_STATEDUMP_FAIL", LTTNG_ERR_REGEN_STATEDUMP_FAIL) + .value("LTTNG_ERR_REGEN_STATEDUMP_NOMEM", LTTNG_ERR_REGEN_STATEDUMP_NOMEM) + .value("LTTNG_ERR_NOT_SNAPSHOT_SESSION", LTTNG_ERR_NOT_SNAPSHOT_SESSION) + .value("LTTNG_ERR_INVALID_TRIGGER", LTTNG_ERR_INVALID_TRIGGER) + .value("LTTNG_ERR_TRIGGER_EXISTS", LTTNG_ERR_TRIGGER_EXISTS) + .value("LTTNG_ERR_TRIGGER_NOT_FOUND", LTTNG_ERR_TRIGGER_NOT_FOUND) + .value("LTTNG_ERR_COMMAND_CANCELLED", LTTNG_ERR_COMMAND_CANCELLED) + .value("LTTNG_ERR_ROTATION_PENDING", LTTNG_ERR_ROTATION_PENDING) + .value("LTTNG_ERR_ROTATION_NOT_AVAILABLE", LTTNG_ERR_ROTATION_NOT_AVAILABLE) + .value("LTTNG_ERR_ROTATION_SCHEDULE_SET", LTTNG_ERR_ROTATION_SCHEDULE_SET) + .value("LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET", LTTNG_ERR_ROTATION_SCHEDULE_NOT_SET) + .value("LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP", LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP) + .value("LTTNG_ERR_ROTATION_WRONG_VERSION", LTTNG_ERR_ROTATION_WRONG_VERSION) + .value("LTTNG_ERR_NO_SESSION_OUTPUT", LTTNG_ERR_NO_SESSION_OUTPUT) + .value("LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY", LTTNG_ERR_ROTATION_NOT_AVAILABLE_RELAY) + .value("LTTNG_ERR_AGENT_TRACING_DISABLED", LTTNG_ERR_AGENT_TRACING_DISABLED) + .value("LTTNG_ERR_PROBE_LOCATION_INVAL", LTTNG_ERR_PROBE_LOCATION_INVAL) + .value("LTTNG_ERR_ELF_PARSING", LTTNG_ERR_ELF_PARSING) + .value("LTTNG_ERR_SDT_PROBE_SEMAPHORE", LTTNG_ERR_SDT_PROBE_SEMAPHORE) + .value("LTTNG_ERR_ROTATION_FAIL_CONSUMER", LTTNG_ERR_ROTATION_FAIL_CONSUMER) + .value("LTTNG_ERR_ROTATE_RENAME_FAIL_CONSUMER", LTTNG_ERR_ROTATE_RENAME_FAIL_CONSUMER) + .value( + "LTTNG_ERR_ROTATION_PENDING_LOCAL_FAIL_CONSUMER", + LTTNG_ERR_ROTATION_PENDING_LOCAL_FAIL_CONSUMER) + .value( + "LTTNG_ERR_ROTATION_PENDING_RELAY_FAIL_CONSUMER", + LTTNG_ERR_ROTATION_PENDING_RELAY_FAIL_CONSUMER) + .value("LTTNG_ERR_MKDIR_FAIL_CONSUMER", LTTNG_ERR_MKDIR_FAIL_CONSUMER) + .value("LTTNG_ERR_CHAN_NOT_FOUND", LTTNG_ERR_CHAN_NOT_FOUND) + .value("LTTNG_ERR_SNAPSHOT_UNSUPPORTED", LTTNG_ERR_SNAPSHOT_UNSUPPORTED) + .value("LTTNG_ERR_SESSION_NOT_EXIST", LTTNG_ERR_SESSION_NOT_EXIST) + .value("LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER", LTTNG_ERR_CREATE_TRACE_CHUNK_FAIL_CONSUMER) + .value("LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER", LTTNG_ERR_CLOSE_TRACE_CHUNK_FAIL_CONSUMER) + .value("LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER", LTTNG_ERR_TRACE_CHUNK_EXISTS_FAIL_CONSUMER) + .value("LTTNG_ERR_INVALID_PROTOCOL", LTTNG_ERR_INVALID_PROTOCOL) + .value("LTTNG_ERR_FILE_CREATION_ERROR", LTTNG_ERR_FILE_CREATION_ERROR) + .value("LTTNG_ERR_TIMER_STOP_ERROR", LTTNG_ERR_TIMER_STOP_ERROR) + .value("LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL", LTTNG_ERR_ROTATION_NOT_AVAILABLE_KERNEL) + .value("LTTNG_ERR_CLEAR_RELAY_DISALLOWED", LTTNG_ERR_CLEAR_RELAY_DISALLOWED) + .value("LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY", LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY) + .value("LTTNG_ERR_CLEAR_FAIL_CONSUMER", LTTNG_ERR_CLEAR_FAIL_CONSUMER) + .value("LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR", LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR) + .value("LTTNG_ERR_USER_NOT_FOUND", LTTNG_ERR_USER_NOT_FOUND) + .value("LTTNG_ERR_GROUP_NOT_FOUND", LTTNG_ERR_GROUP_NOT_FOUND) + .value("LTTNG_ERR_UNSUPPORTED_DOMAIN", LTTNG_ERR_UNSUPPORTED_DOMAIN) + .value( + "LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY", + LTTNG_ERR_PROCESS_ATTR_TRACKER_INVALID_TRACKING_POLICY) + .value( + "LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD", + LTTNG_ERR_EVENT_NOTIFIER_GROUP_NOTIFICATION_FD) + .value("LTTNG_ERR_INVALID_CAPTURE_EXPRESSION", LTTNG_ERR_INVALID_CAPTURE_EXPRESSION) + .value("LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION", LTTNG_ERR_EVENT_NOTIFIER_REGISTRATION) + .value("LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING", LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING) + .value( + "LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL", + LTTNG_ERR_EVENT_NOTIFIER_ERROR_ACCOUNTING_FULL) + .value("LTTNG_ERR_INVALID_ERROR_QUERY_TARGET", LTTNG_ERR_INVALID_ERROR_QUERY_TARGET) + .value("LTTNG_ERR_BUFFER_FLUSH_FAILED", LTTNG_ERR_BUFFER_FLUSH_FAILED) + .value("LTTNG_ERR_NR", LTTNG_ERR_NR) + .value("LTTNG_ERR_PID_TRACKED", LTTNG_ERR_PID_TRACKED) + .value("LTTNG_ERR_PID_NOT_TRACKED", LTTNG_ERR_PID_NOT_TRACKED) + .export_values(); +#endif // LTTNGPY_DISABLED +} diff --git a/lttngpy/src/lttngpy/channel.cpp b/lttngpy/src/lttngpy/channel.cpp new file mode 100644 index 00000000..1402f318 --- /dev/null +++ b/lttngpy/src/lttngpy/channel.cpp @@ -0,0 +1,87 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "lttngpy/channel.hpp" + +namespace lttngpy +{ + +int enable_channel( + const std::string & session_name, + const enum lttng_domain_type domain_type, + const enum lttng_buffer_type buffer_type, + const std::string & channel_name, + const std::optional overwrite, + const std::optional subbuf_size, + const std::optional num_subbuf, + const std::optional switch_timer_interval, + const std::optional read_timer_interval, + const std::optional output) +{ + struct lttng_domain domain {}; + domain.type = domain_type; + domain.buf_type = buffer_type; + + struct lttng_handle * handle = lttng_create_handle(session_name.c_str(), &domain); + if (nullptr == handle) { + return -LTTNG_ERR_UNK; + } + + struct lttng_channel * chan = lttng_channel_create(&domain); + if (nullptr == chan) { + return -LTTNG_ERR_UNK; + } + /** + * Configure channel. + * + * lttng_channel_create() sets default values for the attributes depending on the domain type + * and/or buffer type, so set them only if needed. + */ + channel_name.copy(chan->name, LTTNG_SYMBOL_NAME_LEN); + if (overwrite.has_value()) { + chan->attr.overwrite = overwrite.value(); + } + if (subbuf_size.has_value()) { + chan->attr.subbuf_size = subbuf_size.value(); + } + if (num_subbuf.has_value()) { + chan->attr.num_subbuf = num_subbuf.value(); + } + if (switch_timer_interval.has_value()) { + chan->attr.switch_timer_interval = switch_timer_interval.value(); + } + if (read_timer_interval.has_value()) { + chan->attr.read_timer_interval = read_timer_interval.value(); + } + if (output.has_value()) { + chan->attr.output = output.value(); + } + + int ret = lttng_enable_channel(handle, chan); + lttng_channel_destroy(chan); + lttng_destroy_handle(handle); + return ret; +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/channel.hpp b/lttngpy/src/lttngpy/channel.hpp new file mode 100644 index 00000000..bfc6f03e --- /dev/null +++ b/lttngpy/src/lttngpy/channel.hpp @@ -0,0 +1,57 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__CHANNEL_HPP_ +#define LTTNGPY__CHANNEL_HPP_ + +#include + +#include + +namespace lttngpy +{ + +/** + * Enable channel. + * + * \param session_name the session name + * \param domain_type the domain type + * \param buffer_type the buffer type + * \param channel_name the channel name + * \param overwrite the overwrite attribute (-1: session default, 1: overwrite, 0: discard), or + * `std::nullopt` for the default value + * \param subbuf_size the subbuffer size in bytes (power of 2), or `std::nullopt` for the default + * value + * \param num_subbuf the number of subbuffers (power of 2), or `std::nullopt` for the default value + * \param switch_timer_interval the switch timer internal in usec (0 to disable), or `std::nullopt` + * for the default value + * \param read_timer_interval the read timer internal in usec (0 to disable), or `std::nullopt` for + * the default value + * \return 0 on success, else a negative LTTng error code + */ +int enable_channel( + const std::string & session_name, + const enum lttng_domain_type domain_type, + const enum lttng_buffer_type buffer_type, + const std::string & channel_name, + const std::optional overwrite, + const std::optional subbuf_size, + const std::optional num_subbuf, + const std::optional switch_timer_interval, + const std::optional read_timer_interval, + const std::optional output); + +} // namespace lttngpy + +#endif // LTTNGPY__CHANNEL_HPP_ diff --git a/lttngpy/src/lttngpy/config.hpp.in b/lttngpy/src/lttngpy/config.hpp.in new file mode 100644 index 00000000..ef9366ae --- /dev/null +++ b/lttngpy/src/lttngpy/config.hpp.in @@ -0,0 +1,21 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__CONFIG_HPP_ +#define LTTNGPY__CONFIG_HPP_ + +#cmakedefine LTTNGPY_DISABLED @LTTNGPY_DISABLED@ +#define LTTNG_CTL_VERSION "@LTTNG_CTL_VERSION@" + +#endif // LTTNGPY__CONFIG_HPP_ diff --git a/lttngpy/src/lttngpy/context_app.cpp b/lttngpy/src/lttngpy/context_app.cpp new file mode 100644 index 00000000..9e24dc62 --- /dev/null +++ b/lttngpy/src/lttngpy/context_app.cpp @@ -0,0 +1,58 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include + +#include "lttngpy/context_app.hpp" + +namespace lttngpy +{ + +std::variant get_app_context( + const std::string & app_context_name) +{ + // Extract provider name and type name: $app.PROVIDER:TYPE + std::string::size_type prefix_start = app_context_name.find(app_context_prefix); + if (std::string::npos == prefix_start) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + std::string::size_type provider_name_start = prefix_start + app_context_prefix.length(); + + std::string::size_type provider_ctx_sep = app_context_name.find(app_context_provider_ctx_sep); + if (std::string::npos == provider_ctx_sep) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + std::string::size_type provider_name_len = provider_ctx_sep - provider_name_start; + if (0 == provider_name_len) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + std::string::size_type ctx_name_start = provider_ctx_sep + app_context_provider_ctx_sep.length(); + std::string::size_type ctx_name_len = app_context_name.length() - ctx_name_start; + if (0 == ctx_name_len) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + + std::string provider_name = app_context_name.substr(provider_name_start, provider_name_len); + std::string ctx_name = app_context_name.substr(ctx_name_start, ctx_name_len); + + struct lttng_event_context_app_ctx app_ctx = {}; + app_ctx.provider_name = provider_name; + app_ctx.ctx_name = ctx_name; + return app_ctx; +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/context_app.hpp b/lttngpy/src/lttngpy/context_app.hpp new file mode 100644 index 00000000..9c24e2e9 --- /dev/null +++ b/lttngpy/src/lttngpy/context_app.hpp @@ -0,0 +1,49 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__CONTEXT_APP_HPP_ +#define LTTNGPY__CONTEXT_APP_HPP_ + +#include +#include + +namespace lttngpy +{ + +constexpr std::string_view app_context_prefix = "$app."; +constexpr std::string_view app_context_provider_ctx_sep = ":"; + +/** + * See struct lttng_event_context.u.app_ctx. + */ +struct lttng_event_context_app_ctx +{ + std::string provider_name; + std::string ctx_name; +}; + +/** + * Get provider name and context name from app context type name. + * + * The expected format is: '$app.PROVIDER:TYPE' (see lttng-add-context(1)). + * + * \return the provider name and context name, else a negative LTTng error code (if the name isn't + * using the expected format) +*/ +std::variant get_app_context( + const std::string & app_context_name); + +} // namespace lttngpy + +#endif // LTTNGPY__CONTEXT_APP_HPP_ diff --git a/lttngpy/src/lttngpy/context_lttng.cpp b/lttngpy/src/lttngpy/context_lttng.cpp new file mode 100644 index 00000000..af76bd45 --- /dev/null +++ b/lttngpy/src/lttngpy/context_lttng.cpp @@ -0,0 +1,90 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include + +#include "lttngpy/context_lttng.hpp" + +namespace lttngpy +{ + +/** + * LTTng context name to context type. + * + * The perf counter context types are skipped and have to be handled separately. + * Some other context types are skipped, since they are aliases/equal to another type. + * Finally, the app context type is skipped for now. + * + * For more information, refer to the lttng-ctl API. + */ +std::map context_name_to_context_type = { + {"pid", LTTNG_EVENT_CONTEXT_PID}, + // LTTNG_EVENT_CONTEXT_PERF_COUNTER is equal to LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER + {"procname", LTTNG_EVENT_CONTEXT_PROCNAME}, + {"prio", LTTNG_EVENT_CONTEXT_PRIO}, + {"nice", LTTNG_EVENT_CONTEXT_NICE}, + {"vpid", LTTNG_EVENT_CONTEXT_VPID}, + {"tid", LTTNG_EVENT_CONTEXT_TID}, + {"vtid", LTTNG_EVENT_CONTEXT_VTID}, + {"ppid", LTTNG_EVENT_CONTEXT_PPID}, + {"vppid", LTTNG_EVENT_CONTEXT_VPPID}, + {"pthread_id", LTTNG_EVENT_CONTEXT_PTHREAD_ID}, + {"hostname", LTTNG_EVENT_CONTEXT_HOSTNAME}, + {"ip", LTTNG_EVENT_CONTEXT_IP}, + // LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER is handled separately + // LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER is handled separately + // LTTNG_EVENT_CONTEXT_APP_CONTEXT is handled separately + {"interruptible", LTTNG_EVENT_CONTEXT_INTERRUPTIBLE}, + {"preemptible", LTTNG_EVENT_CONTEXT_PREEMPTIBLE}, + {"need_reschedule", LTTNG_EVENT_CONTEXT_NEED_RESCHEDULE}, + {"migratable", LTTNG_EVENT_CONTEXT_MIGRATABLE}, + {"callstack-kernel", LTTNG_EVENT_CONTEXT_CALLSTACK_KERNEL}, + {"callstack-user", LTTNG_EVENT_CONTEXT_CALLSTACK_USER}, + {"cgroup_ns", LTTNG_EVENT_CONTEXT_CGROUP_NS}, + {"ipc_ns", LTTNG_EVENT_CONTEXT_IPC_NS}, + {"mnt_ns", LTTNG_EVENT_CONTEXT_MNT_NS}, + {"net_ns", LTTNG_EVENT_CONTEXT_NET_NS}, + {"pid_ns", LTTNG_EVENT_CONTEXT_PID_NS}, + {"user_ns", LTTNG_EVENT_CONTEXT_USER_NS}, + {"uts_ns", LTTNG_EVENT_CONTEXT_UTS_NS}, + {"uid", LTTNG_EVENT_CONTEXT_UID}, + {"euid", LTTNG_EVENT_CONTEXT_EUID}, + {"suid", LTTNG_EVENT_CONTEXT_SUID}, + {"gid", LTTNG_EVENT_CONTEXT_GID}, + {"egid", LTTNG_EVENT_CONTEXT_EGID}, + {"sgid", LTTNG_EVENT_CONTEXT_SGID}, + {"vuid", LTTNG_EVENT_CONTEXT_VUID}, + {"veuid", LTTNG_EVENT_CONTEXT_VEUID}, + {"vsuid", LTTNG_EVENT_CONTEXT_VSUID}, + {"vgid", LTTNG_EVENT_CONTEXT_VGID}, + {"vegid", LTTNG_EVENT_CONTEXT_VEGID}, + {"vsgid", LTTNG_EVENT_CONTEXT_VSGID}, + {"time_ns", LTTNG_EVENT_CONTEXT_TIME_NS} +}; + +std::optional get_lttng_context_type( + const std::string & lttng_context_field_name) +{ + const auto & it = context_name_to_context_type.find(lttng_context_field_name); + if (it == std::end(lttngpy::context_name_to_context_type)) { + return std::nullopt; + } + return it->second; +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/context_lttng.hpp b/lttngpy/src/lttngpy/context_lttng.hpp new file mode 100644 index 00000000..3f81221d --- /dev/null +++ b/lttngpy/src/lttngpy/context_lttng.hpp @@ -0,0 +1,36 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__CONTEXT_LTTNG_HPP_ +#define LTTNGPY__CONTEXT_LTTNG_HPP_ + +#include + +#include +#include + +namespace lttngpy +{ + +/** + * Get LTTng context type from name. + * + * \return the context type, or `std::nullopt` on failure (if the name isn't known) +*/ +std::optional get_lttng_context_type( + const std::string & lttng_context_field_name); + +} // namespace lttngpy + +#endif // LTTNGPY__CONTEXT_LTTNG_HPP_ diff --git a/lttngpy/src/lttngpy/context_perf.cpp b/lttngpy/src/lttngpy/context_perf.cpp new file mode 100644 index 00000000..b4084c37 --- /dev/null +++ b/lttngpy/src/lttngpy/context_perf.cpp @@ -0,0 +1,296 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include +#include + +#include "lttngpy/context_perf.hpp" +#include "lttngpy/utils.hpp" + +namespace lttngpy +{ + +/** + * Leave the name as an empty string here, we'll fill it in automatically afterwards. + * + * The last item is the padding. + */ +#define _CTX(ctx_type, counter_id) lttng_event_perf_counter_ctx{ctx_type, counter_id, "", {}} +#define _CTX_HW(counter_id) _CTX(PERF_TYPE_HARDWARE, counter_id) +#define _CTX_HW_CACHE(cache_id) _CTX(PERF_TYPE_HW_CACHE, cache_id) +#define _CTX_SW(counter_id) _CTX(PERF_TYPE_SOFTWARE, counter_id) +#define CTX_HW(name, counter_id) {name, _CTX_HW(counter_id)} +#define CTX_SW(name, counter_id) {name, _CTX_SW(counter_id)} + +/** + * Get 'config' value for a cache/op/result combination. + * + * See perf_event_open(2) for 'config' equation for type PERF_TYPE_HW_CACHE. + */ +#define _CTX_HW_CACHE_OP_RESULT(cache_id, op_id, result_id) \ + (cache_id) | (op_id << 8) | (result_id << 16) +#define CTX_HW_CACHE_OP_RESULT(name, cache_id, op_id, result_id) \ + {name, _CTX_HW_CACHE(_CTX_HW_CACHE_OP_RESULT(cache_id, op_id, result_id))} + +/** + * Each cache counter has 6 different variants: read/write/prefetch and access/miss (3 * 2 = 6). + */ +#define CTX_HW_CACHE(name, cache_id) \ + CTX_HW_CACHE_OP_RESULT( \ + name "-loads", cache_id, \ + PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_ACCESS), \ + CTX_HW_CACHE_OP_RESULT( \ + name "-load-misses", cache_id, \ + PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_RESULT_MISS), \ + CTX_HW_CACHE_OP_RESULT( \ + name "-stores", cache_id, \ + PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_ACCESS), \ + CTX_HW_CACHE_OP_RESULT( \ + name "-store-misses", cache_id, \ + PERF_COUNT_HW_CACHE_OP_WRITE, PERF_COUNT_HW_CACHE_RESULT_MISS), \ + CTX_HW_CACHE_OP_RESULT( \ + name "-prefetches", cache_id, \ + PERF_COUNT_HW_CACHE_OP_PREFETCH, PERF_COUNT_HW_CACHE_RESULT_ACCESS), \ + CTX_HW_CACHE_OP_RESULT( \ + name "-prefetch-misses", cache_id, \ + PERF_COUNT_HW_CACHE_OP_PREFETCH, PERF_COUNT_HW_CACHE_RESULT_MISS) + +/** + * LTTng perf counter name to perf counter context struct. + * + * For more information, refer to the lttng-ctl API and perf API. + * + * LTTng suppports 'perf:[counter name]' counters as old/backwards-compatible names for + * 'perf:{cpu,thread}:[counter name]', but we leave them out here. + */ +std::map perf_counter_name_to_perf_context = { + // Per-CPU counters: hardware + CTX_HW("perf:cpu:cpu-cycles", PERF_COUNT_HW_CPU_CYCLES), + CTX_HW("perf:cpu:cycles", PERF_COUNT_HW_CPU_CYCLES), // Alias + CTX_HW("perf:cpu:instructions", PERF_COUNT_HW_INSTRUCTIONS), + CTX_HW("perf:cpu:cache-references", PERF_COUNT_HW_CACHE_REFERENCES), + CTX_HW("perf:cpu:cache-misses", PERF_COUNT_HW_CACHE_MISSES), + CTX_HW("perf:cpu:branch-instructions", PERF_COUNT_HW_BRANCH_INSTRUCTIONS), + CTX_HW("perf:cpu:branches", PERF_COUNT_HW_BRANCH_INSTRUCTIONS), // Alias + CTX_HW("perf:cpu:branch-misses", PERF_COUNT_HW_BRANCH_MISSES), + CTX_HW("perf:cpu:bus-cycles", PERF_COUNT_HW_BUS_CYCLES), + CTX_HW("perf:cpu:stalled-cycles-frontend", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND), + CTX_HW("perf:cpu:idle-cycles-frontend", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND), // Alias + CTX_HW("perf:cpu:stalled-cycles-backend", PERF_COUNT_HW_STALLED_CYCLES_BACKEND), + CTX_HW("perf:cpu:idle-cycles-backend", PERF_COUNT_HW_STALLED_CYCLES_BACKEND), + + // Per-CPU counters: hardware cache + CTX_HW_CACHE("perf:cpu:L1-dcache", PERF_COUNT_HW_CACHE_L1D), + CTX_HW_CACHE("perf:cpu:L1-icache", PERF_COUNT_HW_CACHE_L1I), + CTX_HW_CACHE("perf:cpu:LLC", PERF_COUNT_HW_CACHE_LL), + CTX_HW_CACHE("perf:cpu:dTLB", PERF_COUNT_HW_CACHE_DTLB), + CTX_HW_CACHE_OP_RESULT( + "perf:cpu:iTLB-loads", + PERF_COUNT_HW_CACHE_ITLB, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_ACCESS), + CTX_HW_CACHE_OP_RESULT( + "perf:cpu:iTLB-load-misses", + PERF_COUNT_HW_CACHE_ITLB, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_MISS), + CTX_HW_CACHE_OP_RESULT( + "perf:cpu:branch-loads", + PERF_COUNT_HW_CACHE_BPU, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_ACCESS), + CTX_HW_CACHE_OP_RESULT( + "perf:cpu:branch-load-misses", + PERF_COUNT_HW_CACHE_BPU, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_MISS), + + // Per-CPU counters: software + CTX_SW("perf:cpu:cpu-clock", PERF_COUNT_SW_CPU_CLOCK), + CTX_SW("perf:cpu:task-clock", PERF_COUNT_SW_TASK_CLOCK), + CTX_SW("perf:cpu:page-fault", PERF_COUNT_SW_PAGE_FAULTS), + CTX_SW("perf:cpu:faults", PERF_COUNT_SW_PAGE_FAULTS), // Alias + CTX_SW("perf:cpu:context-switches", PERF_COUNT_SW_CONTEXT_SWITCHES), + CTX_SW("perf:cpu:cs", PERF_COUNT_SW_CONTEXT_SWITCHES), // Alias + CTX_SW("perf:cpu:cpu-migrations", PERF_COUNT_SW_CPU_MIGRATIONS), + CTX_SW("perf:cpu:migrations", PERF_COUNT_SW_CPU_MIGRATIONS), // Alias + CTX_SW("perf:cpu:minor-faults", PERF_COUNT_SW_PAGE_FAULTS_MIN), + CTX_SW("perf:cpu:major-faults", PERF_COUNT_SW_PAGE_FAULTS_MAJ), + CTX_SW("perf:cpu:alignment-faults", PERF_COUNT_SW_ALIGNMENT_FAULTS), + CTX_SW("perf:cpu:emulation-faults", PERF_COUNT_SW_EMULATION_FAULTS), + + // Per-thread counters: hardware + CTX_HW("perf:thread:cpu-cycles", PERF_COUNT_HW_CPU_CYCLES), + CTX_HW("perf:thread:clock", PERF_COUNT_HW_CPU_CYCLES), + CTX_HW("perf:thread:instructions", PERF_COUNT_HW_INSTRUCTIONS), + CTX_HW("perf:thread:cache-references", PERF_COUNT_HW_CACHE_REFERENCES), + CTX_HW("perf:thread:cache-misses", PERF_COUNT_HW_CACHE_MISSES), + CTX_HW("perf:thread:branch-instructions", PERF_COUNT_HW_BRANCH_INSTRUCTIONS), + CTX_HW("perf:thread:branches", PERF_COUNT_HW_BRANCH_INSTRUCTIONS), // Alias + CTX_HW("perf:thread:branch-misses", PERF_COUNT_HW_BRANCH_MISSES), + CTX_HW("perf:thread:bus-cycles", PERF_COUNT_HW_BUS_CYCLES), + CTX_HW("perf:thread:stalled-cycles-frontend", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND), + CTX_HW("perf:thread:idle-cycles-frontend", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND), // Alias + CTX_HW("perf:thread:stalled-cycles-backend", PERF_COUNT_HW_STALLED_CYCLES_BACKEND), + CTX_HW("perf:thread:idle-cycles-backend", PERF_COUNT_HW_STALLED_CYCLES_BACKEND), // Alias + + // Per-thread counters: hardware cache + CTX_HW_CACHE("perf:thread:L1-dcache", PERF_COUNT_HW_CACHE_L1D), + CTX_HW_CACHE("perf:thread:L1-icache", PERF_COUNT_HW_CACHE_L1I), + CTX_HW_CACHE("perf:thread:LLC", PERF_COUNT_HW_CACHE_LL), + CTX_HW_CACHE("perf:thread:dTLB", PERF_COUNT_HW_CACHE_DTLB), + CTX_HW_CACHE_OP_RESULT( + "perf:thread:iTLB-loads", + PERF_COUNT_HW_CACHE_ITLB, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_ACCESS), + CTX_HW_CACHE_OP_RESULT( + "perf:thread:iTLB-load-misses", + PERF_COUNT_HW_CACHE_ITLB, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_MISS), + CTX_HW_CACHE_OP_RESULT( + "perf:thread:branch-loads", + PERF_COUNT_HW_CACHE_BPU, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_ACCESS), + CTX_HW_CACHE_OP_RESULT( + "perf:thread:branch-load-misses", + PERF_COUNT_HW_CACHE_BPU, + PERF_COUNT_HW_CACHE_OP_READ, + PERF_COUNT_HW_CACHE_RESULT_MISS), + + // Per-thread counters: software + CTX_SW("perf:thread:cpu-clock", PERF_COUNT_SW_CPU_CLOCK), + CTX_SW("perf:thread:task-clock", PERF_COUNT_SW_TASK_CLOCK), + CTX_SW("perf:thread:page-fault", PERF_COUNT_SW_PAGE_FAULTS), + CTX_SW("perf:thread:faults", PERF_COUNT_SW_PAGE_FAULTS), // Alias + CTX_SW("perf:thread:context-switches", PERF_COUNT_SW_CONTEXT_SWITCHES), + CTX_SW("perf:thread:cs", PERF_COUNT_SW_CONTEXT_SWITCHES), // Alias + CTX_SW("perf:thread:cpu-migrations", PERF_COUNT_SW_CPU_MIGRATIONS), + CTX_SW("perf:thread:migrations", PERF_COUNT_SW_CPU_MIGRATIONS), // Alias + CTX_SW("perf:thread:minor-faults", PERF_COUNT_SW_PAGE_FAULTS_MIN), + CTX_SW("perf:thread:major-faults", PERF_COUNT_SW_PAGE_FAULTS_MAJ), + CTX_SW("perf:thread:alignment-faults", PERF_COUNT_SW_ALIGNMENT_FAULTS), + CTX_SW("perf:thread:emulation-faults", PERF_COUNT_SW_EMULATION_FAULTS), +}; + +std::variant _get_perf_raw_counter_context( + const std::string & perf_counter_name, + const bool is_cpu_raw_counter, + const bool is_thread_raw_counter) +{ + struct lttng_event_perf_counter_ctx context = {}; + context.type = lttngpy::PERF_TYPE_RAW; + + // Extract mask (NNN) and name: perf:{cpu,thread}:raw:rNNN:NAME + std::string mask_prefix; + if (is_cpu_raw_counter) { + mask_prefix = + std::string(perf_counter_cpu_prefix) + + std::string(perf_counter_raw_prefix) + + std::string(perf_counter_mask_prefix); + } else if (is_thread_raw_counter) { + mask_prefix = + std::string(perf_counter_thread_prefix) + + std::string(perf_counter_raw_prefix) + + std::string(perf_counter_mask_prefix); + } else { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + std::string::size_type prefix_start = perf_counter_name.find(mask_prefix); + if (std::string::npos == prefix_start) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + std::string::size_type mask_start = prefix_start + mask_prefix.length(); + + std::string::size_type mask_name_sep = + perf_counter_name.find(perf_counter_mask_name_sep, mask_start); + if (std::string::npos == mask_name_sep) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + std::string::size_type mask_len = mask_name_sep - mask_start; + // Mask should not be empty + if (0 == mask_len) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + + std::string::size_type name_start = mask_name_sep + perf_counter_mask_name_sep.length(); + std::string::size_type name_len = perf_counter_name.length() - name_start; + // Name should not be empty + if (0 == name_len) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + + std::string mask_str = perf_counter_name.substr(mask_start, mask_len); + std::string name = perf_counter_name.substr(name_start, name_len); + // Should not have another ':' in the name, after the sep between mask and name + if (std::string::npos != name.find(':')) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + + // Mask value should be hex (see perf-record(1)) + const auto mask_opt = lttngpy::optional_stoull(mask_str, 16); + if (!mask_opt.has_value()) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + context.config = mask_opt.value(); + name.copy(context.name, LTTNG_SYMBOL_NAME_LEN); + return context; +} + +std::variant _get_perf_hw_sw_counter_context( + const std::string & perf_counter_name) +{ + const auto & it = lttngpy::perf_counter_name_to_perf_context.find(perf_counter_name); + if (it == std::end(lttngpy::perf_counter_name_to_perf_context)) { + return -LTTNG_ERR_UST_CONTEXT_INVAL; + } + + /** + * lttng_event_perf_counter_ctx.name should be the LTTng perf context field name with ':' and '-' + * replaced with '_'. + */ + struct lttng_event_perf_counter_ctx context = it->second; + std::string name(perf_counter_name); + std::replace(std::begin(name), std::end(name), ':', '_'); + std::replace(std::begin(name), std::end(name), '-', '_'); + name.copy(context.name, LTTNG_SYMBOL_NAME_LEN); + return context; +} + +std::variant get_perf_counter_context( + const std::string & perf_counter_name) +{ + // Check if it's a raw counter first + const bool is_cpu_raw_counter = lttngpy::starts_with( + perf_counter_name, + std::string(perf_counter_cpu_prefix) + std::string(perf_counter_raw_prefix)); + const bool is_thread_raw_counter = lttngpy::starts_with( + perf_counter_name, + std::string(perf_counter_thread_prefix) + std::string(perf_counter_raw_prefix)); + if (is_cpu_raw_counter || is_thread_raw_counter) { + return _get_perf_raw_counter_context( + perf_counter_name, is_cpu_raw_counter, is_thread_raw_counter); + } + // Otherwise it's a normal hardware or software counter + return _get_perf_hw_sw_counter_context(perf_counter_name); +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/context_perf.hpp b/lttngpy/src/lttngpy/context_perf.hpp new file mode 100644 index 00000000..a2c0f502 --- /dev/null +++ b/lttngpy/src/lttngpy/context_perf.hpp @@ -0,0 +1,143 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__CONTEXT_PERF_HPP_ +#define LTTNGPY__CONTEXT_PERF_HPP_ + +#include + +#include +#include + +namespace lttngpy +{ + +/** + * See perf ABI (include/uapi/linux/perf_event.h). + * + * We/LTTng do not support all of the types here. + */ +enum perf_type_id +{ + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_TRACEPOINT = 2, // Unused here + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, // Unused here + PERF_TYPE_BREAKPOINT = 5, // Unused here + PERF_TYPE_MAX, /* non-ABI */ +}; + +/** + * See perf ABI (include/uapi/linux/perf_event.h). + * + * We/LTTng do not support all of the types here. + */ +enum perf_hw_cache_id +{ + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + PERF_COUNT_HW_CACHE_NODE = 6, // Unused here + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +/** + * See perf ABI (include/uapi/linux/perf_event.h). + * + * We/LTTng do not support all of the types here. + */ +enum perf_hw_cache_op_id +{ + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +/** + * See perf ABI (include/uapi/linux/perf_event.h). + * + * We/LTTng do not support all of the types here. + */ +enum perf_hw_cache_op_result_id +{ + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +/** + * See perf ABI (include/uapi/linux/perf_event.h). + * + * We/LTTng do not support all of the types here. + */ +enum perf_hw_id +{ + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, + PERF_COUNT_HW_REF_CPU_CYCLES = 9, // Unused + PERF_COUNT_HW_MAX, /* non-ABI */ +}; + +/** + * See perf ABI (include/uapi/linux/perf_event.h). + * + * We/LTTng do not support all of the types here. + */ +enum perf_sw_ids +{ + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, + PERF_COUNT_SW_EMULATION_FAULTS = 8, + PERF_COUNT_SW_DUMMY = 9, // Unused + PERF_COUNT_SW_BPF_OUTPUT = 10, // Unused + PERF_COUNT_SW_CGROUP_SWITCHES = 11, // Unused + PERF_COUNT_SW_MAX, /* non-ABI */ +}; + +constexpr std::string_view perf_counter_prefix = "perf:"; +constexpr std::string_view perf_counter_cpu_prefix = "perf:cpu:"; +constexpr std::string_view perf_counter_thread_prefix = "perf:thread:"; +constexpr std::string_view perf_counter_raw_prefix = "raw:"; +constexpr std::string_view perf_counter_mask_prefix = "r"; +constexpr std::string_view perf_counter_mask_name_sep = ":"; + +/** + * Get perf counter context from name. + * + * \return the perf counter context, else a negative LTTng error code (if the name isn't known) +*/ +std::variant get_perf_counter_context( + const std::string & perf_counter_name); + +} // namespace lttngpy + +#endif // LTTNGPY__CONTEXT_PERF_HPP_ diff --git a/lttngpy/src/lttngpy/event.cpp b/lttngpy/src/lttngpy/event.cpp new file mode 100644 index 00000000..dac98a56 --- /dev/null +++ b/lttngpy/src/lttngpy/event.cpp @@ -0,0 +1,211 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "lttngpy/context_app.hpp" +#include "lttngpy/context_lttng.hpp" +#include "lttngpy/context_perf.hpp" +#include "lttngpy/event.hpp" +#include "lttngpy/utils.hpp" + +namespace lttngpy +{ + +int enable_events( + const std::string & session_name, + const enum lttng_domain_type domain_type, + const std::string & channel_name, + const std::set & events) +{ + if (events.empty()) { + return 0; + } + + // We do not actually need to specify a buffer type for this + struct lttng_domain domain {}; + domain.type = domain_type; + + struct lttng_handle * handle = lttng_create_handle(session_name.c_str(), &domain); + if (nullptr == handle) { + return -LTTNG_ERR_UNK; + } + + int ret = 0; + for (const auto & event_name : events) { + struct lttng_event * event = lttng_event_create(); + if (nullptr == event) { + ret = -LTTNG_ERR_UNK; + break; + } + event_name.copy(event->name, LTTNG_SYMBOL_NAME_LEN); + event->type = LTTNG_EVENT_TRACEPOINT; + + ret = lttng_enable_event(handle, event, channel_name.c_str()); + lttng_event_destroy(event); + if (0 != ret) { + break; + } + } + + lttng_destroy_handle(handle); + return ret; +} + +std::variant> get_tracepoints(const enum lttng_domain_type domain_type) +{ + // We do not actually need to specify a buffer type for this + struct lttng_domain domain {}; + domain.type = domain_type; + + struct lttng_handle * handle = lttng_create_handle(nullptr, &domain); + if (nullptr == handle) { + // This seems to be what lttng-ctl itself returns in this case + return -LTTNG_ERR_NOMEM; + } + + struct lttng_event * events = nullptr; + int ret = lttng_list_tracepoints(handle, &events); + std::variant> tracepoints_var = ret; + if (0 <= ret) { + std::set tracepoints = {}; + const int num_events = ret; + for (int i = 0; i < num_events; i++) { + tracepoints.insert(events[i].name); + } + tracepoints_var = tracepoints; + } + + std::free(events); + lttng_destroy_handle(handle); + return tracepoints_var; +} + +int _fill_in_event_context( + const std::string & context_field, + const enum lttng_domain_type domain_type, + struct lttng_event_context * context) +{ + // Context type + enum lttng_event_context_type ctx; + if (lttngpy::starts_with(context_field, std::string(lttngpy::app_context_prefix))) { + // App context + ctx = LTTNG_EVENT_CONTEXT_APP_CONTEXT; + const auto app_context_var = lttngpy::get_app_context(context_field); + if (std::holds_alternative(app_context_var)) { + return std::get(app_context_var); + } + const auto app_context = std::get(app_context_var); + // The caller must free the char arrays after use + context->u.app_ctx.provider_name = strdup(app_context.provider_name.c_str()); + context->u.app_ctx.ctx_name = strdup(app_context.ctx_name.c_str()); + } else if (lttngpy::starts_with(context_field, std::string(lttngpy::perf_counter_prefix))) { + // Perf counter context + const bool is_cpu_counter = + lttngpy::starts_with(context_field, std::string(lttngpy::perf_counter_cpu_prefix)); + const bool is_thread_counter = + lttngpy::starts_with(context_field, std::string(lttngpy::perf_counter_thread_prefix)); + if (is_cpu_counter) { + ctx = LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER; + } else if (is_thread_counter) { + ctx = LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER; + } else { + return -LTTNG_ERR_UNK; + } + } else { + // Normal LTTng context + const auto context_type_opt = lttngpy::get_lttng_context_type(context_field); + if (!context_type_opt.has_value()) { + switch (domain_type) { + case LTTNG_DOMAIN_KERNEL: + return -LTTNG_ERR_KERN_CONTEXT_FAIL; + case LTTNG_DOMAIN_UST: + return -LTTNG_ERR_UST_CONTEXT_INVAL; + case LTTNG_DOMAIN_NONE: // Fall through + case LTTNG_DOMAIN_JUL: // Fall through + case LTTNG_DOMAIN_LOG4J: // Fall through + case LTTNG_DOMAIN_PYTHON: // Fall through + default: + return -LTTNG_ERR_UNK; + } + } + ctx = context_type_opt.value(); + } + context->ctx = ctx; + + // Perf counters + if (context->ctx == LTTNG_EVENT_CONTEXT_PERF_CPU_COUNTER || + context->ctx == LTTNG_EVENT_CONTEXT_PERF_THREAD_COUNTER) + { + const auto counter_context_type_var = lttngpy::get_perf_counter_context(context_field); + if (std::holds_alternative(counter_context_type_var)) { + return std::get(counter_context_type_var); + } + context->u.perf_counter = std::get(counter_context_type_var); + } + return 0; +} + +int add_contexts( + const std::string & session_name, + const enum lttng_domain_type domain_type, + const std::string & channel_name, + const std::set & context_fields) +{ + if (context_fields.empty()) { + return 0; + } + + // We do not actually need to specify a buffer type for this + struct lttng_domain domain {}; + domain.type = domain_type; + + struct lttng_handle * handle = lttng_create_handle(session_name.c_str(), &domain); + if (nullptr == handle) { + return -LTTNG_ERR_UNK; + } + + int ret = 0; + for (const auto & context_field : context_fields) { + struct lttng_event_context context = {}; + ret = _fill_in_event_context(context_field, domain_type, &context); + if (0 == ret) { + ret = lttng_add_context(handle, &context, nullptr, channel_name.c_str()); + } + + // Free app context strings + if (LTTNG_EVENT_CONTEXT_APP_CONTEXT == context.ctx) { + std::free(context.u.app_ctx.provider_name); + std::free(context.u.app_ctx.ctx_name); + } + + if (0 != ret) { + break; + } + } + + lttng_destroy_handle(handle); + return ret; +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/event.hpp b/lttngpy/src/lttngpy/event.hpp new file mode 100644 index 00000000..82e7f100 --- /dev/null +++ b/lttngpy/src/lttngpy/event.hpp @@ -0,0 +1,69 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__EVENT_HPP_ +#define LTTNGPY__EVENT_HPP_ + +#include +#include + +#include +#include +#include + +namespace lttngpy +{ + +/** + * Enable events. + * + * \param session_name the session name + * \param domain_type the domain type + * \param channel_name the channel name + * \param events the set of event names + * \return 0 on success, else a negative LTTng error code + */ +int enable_events( + const std::string & session_name, + const enum lttng_domain_type domain_type, + const std::string & channel_name, + const std::set & events); + +/** + * Get tracepoints. + * + * \param domain_type the domain type + * \return the set of tracepoints, else a negative LTTng error code (e.g., if kernel tracer is not + * available when providing the kernel domain) + */ +std::variant> get_tracepoints(const enum lttng_domain_type domain_type); + +/** + * Add contexts. + * + * \param session_name the session name + * \param domain_type the domain type + * \param channel_name the channel name + * \param context_fields the set of context field names + * \return 0 on success, else a negative LTTng error code + */ +int add_contexts( + const std::string & session_name, + const enum lttng_domain_type domain_type, + const std::string & channel_name, + const std::set & context_fields); + +} // namespace lttngpy + +#endif // LTTNGPY__EVENT_HPP_ diff --git a/lttngpy/src/lttngpy/lttng.cpp b/lttngpy/src/lttngpy/lttng.cpp new file mode 100644 index 00000000..628e5c32 --- /dev/null +++ b/lttngpy/src/lttngpy/lttng.cpp @@ -0,0 +1,27 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "lttngpy/lttng.hpp" + +namespace lttngpy +{ + +bool is_lttng_session_daemon_alive() +{ + return 1 == lttng_session_daemon_alive(); +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/lttng.hpp b/lttngpy/src/lttngpy/lttng.hpp new file mode 100644 index 00000000..d87cf154 --- /dev/null +++ b/lttngpy/src/lttngpy/lttng.hpp @@ -0,0 +1,32 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__LTTNG_HPP_ +#define LTTNGPY__LTTNG_HPP_ + +#include + +namespace lttngpy +{ + +/** + * Check if session daemon is alive. + * + * \return true if alive, false otherwise + */ +bool is_lttng_session_daemon_alive(); + +} // namespace lttngpy + +#endif // LTTNGPY__LTTNG_HPP_ diff --git a/lttngpy/src/lttngpy/session.cpp b/lttngpy/src/lttngpy/session.cpp new file mode 100644 index 00000000..8fd989cf --- /dev/null +++ b/lttngpy/src/lttngpy/session.cpp @@ -0,0 +1,67 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include +#include +#include + +#include "lttngpy/session.hpp" + +namespace lttngpy +{ + +std::variant> get_session_names() +{ + struct lttng_session * sessions = nullptr; + int ret = lttng_list_sessions(&sessions); + if (0 > ret) { + std::free(sessions); + return ret; + } + + std::set session_names = {}; + const int num_sessions = ret; + for (int i = 0; i < num_sessions; i++) { + session_names.insert(sessions[i].name); + } + std::free(sessions); + return session_names; +} + +int destroy_all_sessions() +{ + const auto & session_names_opt = lttngpy::get_session_names(); + if (std::holds_alternative(session_names_opt)) { + return std::get(session_names_opt); + } + + int overall_ret = 0; + const auto session_names = std::get>(session_names_opt); + for (const auto & session_name : session_names) { + /** + * Try to destroy all sessions, but return the last error code if any session destruction was + * unsuccessful. + */ + int ret = lttng_destroy_session(session_name.c_str()); + if (0 != ret) { + overall_ret = ret; + } + } + return overall_ret; +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/session.hpp b/lttngpy/src/lttngpy/session.hpp new file mode 100644 index 00000000..e8806c48 --- /dev/null +++ b/lttngpy/src/lttngpy/session.hpp @@ -0,0 +1,43 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__SESSION_HPP_ +#define LTTNGPY__SESSION_HPP_ + +#include +#include +#include + +namespace lttngpy +{ + +/** + * Get the currently-existing session names. + * + * \return the set of session names, else a negative LTTng error code + */ +std::variant> get_session_names(); + +/** + * Destroy all sessions. + * + * Tries to destroy all sessions, and reports an error if any session destruction was unsuccessful. + * + * \return 0 on success, else a negative LTTng error code + */ +int destroy_all_sessions(); + +} // namespace lttngpy + +#endif // LTTNGPY__SESSION_HPP_ diff --git a/lttngpy/src/lttngpy/status.cpp b/lttngpy/src/lttngpy/status.cpp new file mode 100644 index 00000000..91270338 --- /dev/null +++ b/lttngpy/src/lttngpy/status.cpp @@ -0,0 +1,30 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lttngpy/config.hpp" +#include "lttngpy/status.hpp" + +namespace lttngpy +{ + +bool is_available() +{ +#ifndef LTTNGPY_DISABLED + return true; +#else + return false; +#endif +} + +} // namespace lttngpy diff --git a/lttngpy/src/lttngpy/status.hpp b/lttngpy/src/lttngpy/status.hpp new file mode 100644 index 00000000..d2c8ca77 --- /dev/null +++ b/lttngpy/src/lttngpy/status.hpp @@ -0,0 +1,35 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__STATUS_HPP_ +#define LTTNGPY__STATUS_HPP_ + +namespace lttngpy +{ + +/** + * Check if lttng-ctl is available. + * + * This is the only function guaranteed to exist in this Python module. If this returns false, then + * it means that no other functions are available. + * + * This is false on non-Linux platforms, or if it was explicitly disabled during build. + * + * \return true if available, false otherwise + */ +bool is_available(); + +} // namespace lttngpy + +#endif // LTTNGPY__STATUS_HPP_ diff --git a/lttngpy/src/lttngpy/utils.hpp b/lttngpy/src/lttngpy/utils.hpp new file mode 100644 index 00000000..07f6643a --- /dev/null +++ b/lttngpy/src/lttngpy/utils.hpp @@ -0,0 +1,47 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LTTNGPY__UTILS_HPP_ +#define LTTNGPY__UTILS_HPP_ + +#include +#include +#include + +namespace lttngpy +{ + +inline bool starts_with(const std::string & str, const std::string & substr) +{ + return std::equal(substr.begin(), substr.end(), str.begin()); +} + +inline std::optional optional_stoull(const std::string & str, int base) +{ + try { + return static_cast(std::stoull(str, nullptr, base)); + } catch (...) { + } + return std::nullopt; +} + +template +inline bool is_int(const std::variant & var) +{ + return std::holds_alternative(var); +} + +} // namespace lttngpy + +#endif // LTTNGPY__UTILS_HPP_ diff --git a/lttngpy/test/test_constants.py b/lttngpy/test/test_constants.py new file mode 100644 index 00000000..7fe56167 --- /dev/null +++ b/lttngpy/test/test_constants.py @@ -0,0 +1,29 @@ +# Copyright 2023 Apex.AI, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from lttngpy import impl as lttngpy + + +class TestConstants(unittest.TestCase): + + def test_version(self): + self.assertRegex(lttngpy.LTTNG_CTL_VERSION, r'^\d+\.\d+\.\d+$') + + def test_error(self): + # Just check a couple to make sure they're present + self.assertEqual(10, lttngpy.LTTNG_OK) + self.assertTrue(0 < len(lttngpy.lttng_strerror(lttngpy.LTTNG_OK))) + self.assertEqual(11, lttngpy.LTTNG_ERR_UNK) diff --git a/lttngpy/test/test_context_app.cpp b/lttngpy/test/test_context_app.cpp new file mode 100644 index 00000000..df314fcf --- /dev/null +++ b/lttngpy/test/test_context_app.cpp @@ -0,0 +1,38 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include "lttngpy/context_app.hpp" +#include "lttngpy/utils.hpp" + +TEST(TestContextApp, invalid_app_context_name) { + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context(""))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context("$app."))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context(":"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context("$app.p"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context("$app.p:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context("$app.:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_app_context("$app.:c"))); +} + +TEST(TestContextApp, valid_app_context_name) { + const auto var = lttngpy::get_app_context("$app.provider:type"); + ASSERT_TRUE(!std::holds_alternative(var)); + const auto context = std::get(var); + EXPECT_EQ("provider", context.provider_name); + EXPECT_EQ("type", context.ctx_name); +} diff --git a/lttngpy/test/test_context_lttng.cpp b/lttngpy/test/test_context_lttng.cpp new file mode 100644 index 00000000..c8433983 --- /dev/null +++ b/lttngpy/test/test_context_lttng.cpp @@ -0,0 +1,31 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include + +#include "lttngpy/context_lttng.hpp" + +TEST(TestContextLttng, invalid_context_name) { + EXPECT_EQ(std::nullopt, lttngpy::get_lttng_context_type("")); + EXPECT_EQ(std::nullopt, lttngpy::get_lttng_context_type("unknown")); +} + +TEST(TestContextLttng, valid_context_name) { + const auto opt = lttngpy::get_lttng_context_type("pid"); + ASSERT_TRUE(opt.has_value()); + EXPECT_EQ(LTTNG_EVENT_CONTEXT_PID, opt.value()); +} diff --git a/lttngpy/test/test_context_perf.cpp b/lttngpy/test/test_context_perf.cpp new file mode 100644 index 00000000..3d7db6e9 --- /dev/null +++ b/lttngpy/test/test_context_perf.cpp @@ -0,0 +1,70 @@ +// Copyright 2023 Apex.AI, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include + +#include "lttngpy/context_perf.hpp" +#include "lttngpy/utils.hpp" + +TEST(TestContextPerf, invalid_context_name) { + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context(""))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("unknown"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:cpu"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:cpu:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:cpu:unknown"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:unknown"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw::"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:r"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:r:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:r1:"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:r:name"))); + EXPECT_TRUE(lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:b1234:name"))); + EXPECT_TRUE( + lttngpy::is_int(lttngpy::get_perf_counter_context("perf:thread:raw:r0110:name:extra"))); +} + +TEST(TestContextPerf, valid_context_name) { + auto var = lttngpy::get_perf_counter_context("perf:thread:task-clock"); + ASSERT_TRUE(!std::holds_alternative(var)); + auto context = std::get(var); + EXPECT_EQ(lttngpy::PERF_TYPE_SOFTWARE, context.type); + EXPECT_EQ(lttngpy::PERF_COUNT_SW_TASK_CLOCK, context.config); + EXPECT_STREQ("perf_thread_task_clock", context.name); + + var = lttngpy::get_perf_counter_context("perf:cpu:dTLB-loads"); + ASSERT_TRUE(!std::holds_alternative(var)); + context = std::get(var); + EXPECT_EQ(lttngpy::PERF_TYPE_HW_CACHE, context.type); + // See equation + EXPECT_EQ( + (lttngpy::PERF_COUNT_HW_CACHE_DTLB) | + (lttngpy::PERF_COUNT_HW_CACHE_OP_READ << 8) | + (lttngpy::PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16), + context.config); + EXPECT_STREQ("perf_cpu_dTLB_loads", context.name); + + var = lttngpy::get_perf_counter_context("perf:thread:raw:r0110:name"); + ASSERT_TRUE(!std::holds_alternative(var)); + context = std::get(var); + EXPECT_EQ(lttngpy::PERF_TYPE_RAW, context.type); + EXPECT_EQ(272, context.config); + EXPECT_STREQ("name", context.name); +} diff --git a/lttngpy/test/test_session.py b/lttngpy/test/test_session.py new file mode 100644 index 00000000..c7732f44 --- /dev/null +++ b/lttngpy/test/test_session.py @@ -0,0 +1,68 @@ +# Copyright 2023 Apex.AI, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import shutil +import tempfile +import unittest + +from lttngpy import impl as lttngpy + + +@unittest.skipIf(not lttngpy.is_lttng_session_daemon_alive(), 'session daemon required') +class TestSession(unittest.TestCase): + + def create_test_tmpdir(self, test_name: str) -> str: + prefix = self.__class__.__name__ + '__' + test_name + return tempfile.mkdtemp(prefix=prefix) + + def test_session_list_create_start_stop_destroy(self): + session_name = 'test_session_list_create_start_stop_destroy' + tmpdir = self.create_test_tmpdir(session_name) + + self.assertSetEqual(set(), lttngpy.get_session_names()) + self.assertEqual(0, lttngpy.lttng_create_session(session_name=session_name, url=tmpdir)) + self.assertSetEqual({session_name}, lttngpy.get_session_names()) + self.assertEqual( + 0, + lttngpy.enable_channel( + session_name=session_name, + domain_type=lttngpy.LTTNG_DOMAIN_UST, + buffer_type=lttngpy.LTTNG_BUFFER_PER_UID, + channel_name='dummy_channel', + overwrite=None, + subbuf_size=None, + num_subbuf=None, + switch_timer_interval=None, + read_timer_interval=None, + output=None, + ), + ) + self.assertEqual(0, lttngpy.lttng_start_tracing(session_name=session_name)) + self.assertEqual(0, lttngpy.lttng_stop_tracing(session_name=session_name)) + self.assertEqual(0, lttngpy.lttng_destroy_session(session_name=session_name)) + self.assertSetEqual(set(), lttngpy.get_session_names()) + + self.assertEqual(0, lttngpy.lttng_create_session(session_name=session_name, url=tmpdir)) + self.assertEqual(0, lttngpy.destroy_all_sessions()) + self.assertSetEqual(set(), lttngpy.get_session_names()) + + shutil.rmtree(tmpdir) + + def test_error(self): + session_name = 'test_error' + self.assertSetEqual(set(), lttngpy.get_session_names()) + self.assertNotEqual(0, lttngpy.lttng_start_tracing(session_name=session_name)) + self.assertNotEqual(0, lttngpy.lttng_stop_tracing(session_name=session_name)) + self.assertNotEqual(0, lttngpy.lttng_destroy_session(session_name=session_name)) + self.assertSetEqual(set(), lttngpy.get_session_names())